/***********************************************************************************/ /* Copyright 2012 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 . */ #include "GRIBIO.h" #include #include #include //for PI #include #include #include #include #include #include using namespace std; namespace mio { /** * @page gribio GRIBIO * @section gribio_format Format and limitations * This plugin reads GRIB (https://en.wikipedia.org/wiki/GRIB) files as produced by meteorological models. * Being based on GRIB API (https://software.ecmwf.int/wiki/display/GRIB/Home), it should support both version 1 and 2 of the format (please note that grib_api must be compiled with Position Independent Code ("fPIC" flag)). * Fields are read based on their marsParam code (this is built as {grib parameter number}.{grib table number} the table being preferably table 2, the parameter being preferably WMO standardized, as in http://rda.ucar.edu/docs/formats/grib/gribdoc/params.html) and levels * (levels description is available at http://www.nco.ncep.noaa.gov/pmb/docs/on388/). Standard COSMO grids are listed at http://zephyr.ucd.ie/mediawiki/index.php/COSMO_GRIB while NCEP are listed at http://www.cpc.ncep.noaa.gov/products/wesley/opn_gribtable.html. Users with access to CSCS can find the up-to-date parameters description under /oprusers/osm/opr/lib/dictionary_cosmo.txt * * Several assumptions/approximations are held/made when reading grids: * - since models usually use rotated latitude/longitude (see http://www.cosmo-model.org/content/model/documentation/core/default.htm , part I, chapter 3.3), the center of the domain can be approximated by a tangential cartesian coordinate system. We therefore don't re-project the lat/lon grid and use it "as is". * - however, no correction for grid rotation is currently performed. If a grid rotation is specified on top of the rotated coordinate system, an error message will be given * - the cell size is computed at the center of the domain. This is performed by retrieving the latitude and longitude increments in the rotated coordinates, computing the point at center+increment in geographic coordinates and computing the equivalent geographic latitude and longitude increment. These increments are then converted to distances along the parallel and meridian at the true center latitude (see https://en.wikipedia.org/wiki/Latitude#The_length_of_a_degree_of_latitude). * - the average cell size (ie: average between x and y) is used to move the center point to the lower left corner. This will be returned as lower left corner geolocalization of the grid. * * This means that close to the center of the grid, coordinates and distances will work as expected, but the distortion will increase when moving away from the center and can become significant. As examples for domain size, cone can look at the MeteoSwiss domain definition at http://www.cosmo-model.org/content/tasks/operational/meteoSwiss/default.htm . * * As a side note, when calling read2DGrid(grid, filename), it will returns the first grid that is found. * * @section gribio_cosmo_partners COSMO Group * This plugin has been developed primarily for reading GRIB files produced by COSMO (http://www.cosmo-model.org/) at MeteoSwiss. * COSMO (COnsortium for Small scale MOdelling) represents a non-hydrostatic limited-area atmospheric model, to be used both for operational and for research applications by the members of the consortium. The Consortium has the following members: * - Germany, DWD, Deutscher Wetterdienst * - Switzerland, MCH, MeteoSchweiz * - Italy, USAM, Ufficio Generale Spazio Aereo e Meteorologia * - Greece, HNMS, Hellenic National Meteorological Service * - Poland, IMGW, Institute of Meteorology and Water Management * - Romania, NMA, National Meteorological Administration * - Russia, RHM, Federal Service for Hydrometeorology and Environmental Monitoring * - Germany, AGeoBw, Amt für GeoInformationswesen der Bundeswehr * - Italy, CIRA, Centro Italiano Ricerche Aerospaziali * - Italy, ARPA-SIMC, ARPA Emilia Romagna Servizio Idro Meteo Clima * - Italy, ARPA Piemonte, Agenzia Regionale per la Protezione Ambientale Piemonte * * @section gribio_units Units * As specified by WMO. * * @section gribio_files_naming Files naming scheme * When a grid is read by providing the filename to open, any file name will obviously work. Otherwise, the file names have to follow the pattern:\n * {GRID2DPREFIX}{ISO8601 numerical UTC date}{GRID2DEXT}\n * By default, GRID2DPREFIX is empty and GRID2DEXT is ".grb". This means that by default, a grib file containing data for 2013-10-15T12:00 would be: * "201310151200.grb". Since the grid extension contains the ".", it is possible to use it for a file name suffix as well. For example, to read files * named like "lfff201310151200_z" one would provide GRID2DPREFIX="lfff" and GRID2DEXT="_z". * * @section gribio_keywords Keywords * This plugin uses the following keywords: * - COORDSYS: coordinate system (see Coords) * - COORDPARAM: extra coordinates parameters (see Coords) * - GRID2DPATH: path where to find the grids * - GRID2DPREFIX: prefix to append when generating a file name for reading (ie: something like "laf" for Cosmo-Analysis-full domain), optional * - GRID2DEXT: grib file extension, or none for no file extension (default: ".grb") * - GRIB_DEM_UPDATE: recompute slope/azimuth from the elevations when reading a DEM (default=false, * that is we use the slope and azimuth included in the GRIB file) * - METEOPATH: path where to find the grids for extracting time series at the given points * - METEOEXT: file extension, or none for no file extension (default: ".grb") * - STATION#: coordinates for virtual stations (if using GRIB as METEO plugin). Each station is given by its coordinates and the closest * grid point will be chosen. Coordinates are given one one line as "lat lon" or "xcoord ycoord epsg_code". If a point leads to duplicate grid points, * it will be removed from the list. * */ const double GRIBIO::plugin_nodata = -999.; //plugin specific nodata value. It can also be read by the plugin (depending on what is appropriate) const double GRIBIO::tz_in = 0.; //GRIB time zone, always UTC const std::string GRIBIO::default_ext=".grb"; //filename extension GRIBIO::GRIBIO(const std::string& configfile) : cfg(configfile), grid2dpath_in(), meteopath_in(), vecPts(), cache_meteo_files(), meteo_ext(default_ext), grid2d_ext(default_ext), grid2d_prefix(), idx_filename(), coordin(), coordinparam(), VW(), DW(), wind_date(), llcorner(), fp(NULL), idx(NULL), latitudeOfNorthernPole(IOUtils::nodata), longitudeOfNorthernPole(IOUtils::nodata), bearing_offset(IOUtils::nodata), cellsize(IOUtils::nodata), factor_x(IOUtils::nodata), factor_y(IOUtils::nodata), indexed(false), meteo_initialized(false), llcorner_initialized(false), update_dem(false) { setOptions(); } GRIBIO::GRIBIO(const Config& cfgreader) : cfg(cfgreader), grid2dpath_in(), meteopath_in(), vecPts(), cache_meteo_files(), meteo_ext(default_ext), grid2d_ext(default_ext), grid2d_prefix(), idx_filename(), coordin(), coordinparam(), VW(), DW(), wind_date(), llcorner(), fp(NULL), idx(NULL), latitudeOfNorthernPole(IOUtils::nodata), longitudeOfNorthernPole(IOUtils::nodata), bearing_offset(IOUtils::nodata), cellsize(IOUtils::nodata), factor_x(IOUtils::nodata), factor_y(IOUtils::nodata), indexed(false), meteo_initialized(false), llcorner_initialized(false), update_dem(false) { setOptions(); } GRIBIO& GRIBIO::operator=(const GRIBIO& source) { if(this != &source) { fp = NULL; idx = NULL; grid2dpath_in = source.grid2dpath_in; meteopath_in = source.meteopath_in; vecPts = source.vecPts; cache_meteo_files = source.cache_meteo_files; meteo_ext = source.meteo_ext; grid2d_ext = source.grid2d_ext; grid2d_prefix = source.grid2d_prefix; idx_filename = source.idx_filename; coordin = source.coordin; coordinparam = source.coordinparam; VW = source.VW; DW = source.DW; wind_date = source.wind_date; llcorner = source.llcorner; latitudeOfNorthernPole = source.latitudeOfNorthernPole; longitudeOfNorthernPole = source.longitudeOfNorthernPole; bearing_offset = source.bearing_offset; cellsize = source.cellsize; factor_x = source.factor_x; factor_y = source.factor_y; indexed = source.indexed; meteo_initialized = source.meteo_initialized; llcorner_initialized = source.llcorner_initialized; update_dem = source.update_dem; } return *this; } GRIBIO::~GRIBIO() throw() { cleanup(); } void GRIBIO::setOptions() { std::string coordout, coordoutparam; IOUtils::getProjectionParameters(cfg, coordin, coordinparam, coordout, coordoutparam); const string tmp = cfg.get("GRID2D", "Input", IOUtils::nothrow); if (tmp == "GRIB") { //keep it synchronized with IOHandler.cc for plugin mapping!! cfg.getValue("GRID2DPATH", "Input", grid2dpath_in); cfg.getValue("GRIB_DEM_UPDATE", "Input", update_dem, IOUtils::nothrow); } cfg.getValue("GRID2DPREFIX", "Input", grid2d_prefix, IOUtils::nothrow); cfg.getValue("METEOEXT", "Input", meteo_ext, IOUtils::nothrow); if(meteo_ext=="none") meteo_ext.clear(); cfg.getValue("GRID2DEXT", "Input", grid2d_ext, IOUtils::nothrow); if(grid2d_ext=="none") grid2d_ext.clear(); } void GRIBIO::readStations(std::vector &vecPoints) { cfg.getValue("METEOPATH", "Input", meteopath_in); std::vector vecStation; cfg.getValues("STATION", "INPUT", vecStation); for(size_t ii=0; ii(dataDate/10000), month=static_cast(dataDate/100-year*100), day=static_cast(dataDate-month*100-year*10000); const int hour=static_cast(dataTime/100), minutes=static_cast(dataTime-hour*100); //HACK: handle seconds! base.setDate(year, month, day, hour, minutes, tz_in); //reading offset to base date/time, as used for forecast, computed at time t for t+offset long stepUnits, startStep, endStep; GRIB_CHECK(grib_get_long(h,"stepUnits",&stepUnits),0); GRIB_CHECK(grib_get_long(h,"startStep",&startStep),0); GRIB_CHECK(grib_get_long(h,"endStep",&endStep),0); double step_units; //in julian, ie. in days switch(stepUnits) { case 0: //minutes step_units = 1./(24.*60.); break; case 1: //hours step_units = 1./24.; break; case 2: //days step_units = 1.; break; case 10: //3 hours step_units = 3./24.; break; case 11: //6 hours step_units = 6./24.; break; case 12: //12 hours step_units = 12./24.; break; case 13: //seconds step_units = 1./(24.*3600.); break; case 14: //15 minutes step_units = 15./(24.*60.); break; case 15: //30 minutes step_units = 30./(24.*60.); break; default: std::ostringstream ss; ss << "GRIB file using stepUnits=" << stepUnits << ", which is not supported"; throw InvalidFormatException(ss.str(), AT); } d1 = static_cast(startStep)*step_units; d2 = static_cast(endStep)*step_units; } Coords GRIBIO::getGeolocalization(grib_handle* h, double &cell_x, double &cell_y) { //getting transformation parameters double angleOfRotationInDegrees = 0.; //if the key was not found, we consider there is no rotation grib_get_double(h,"angleOfRotationInDegrees",&angleOfRotationInDegrees); if(angleOfRotationInDegrees!=0.) { throw InvalidArgumentException("Rotated grids not supported!", AT); } double latitudeOfSouthernPole = -90, longitudeOfSouthernPole = -180; grib_get_double(h,"latitudeOfSouthernPoleInDegrees",&latitudeOfSouthernPole); grib_get_double(h,"longitudeOfSouthernPoleInDegrees",&longitudeOfSouthernPole); latitudeOfNorthernPole = -latitudeOfSouthernPole; longitudeOfNorthernPole = longitudeOfSouthernPole+180.; //determining llcorner, urcorner and center coordinates double ll_latitude, ll_longitude, ll_lat, ll_lon; GRIB_CHECK(grib_get_double(h,"latitudeOfFirstGridPointInDegrees",&ll_latitude),0); GRIB_CHECK(grib_get_double(h,"longitudeOfFirstGridPointInDegrees",&ll_longitude),0); Coords::rotatedToTrueLatLon(latitudeOfNorthernPole, longitudeOfNorthernPole, ll_latitude, ll_longitude, ll_lat, ll_lon); double ur_latitude, ur_longitude, ur_lat, ur_lon; GRIB_CHECK(grib_get_double(h,"latitudeOfLastGridPointInDegrees",&ur_latitude),0); GRIB_CHECK(grib_get_double(h,"longitudeOfLastGridPointInDegrees",&ur_longitude),0); Coords::rotatedToTrueLatLon(latitudeOfNorthernPole, longitudeOfNorthernPole, ur_latitude, ur_longitude, ur_lat, ur_lon); double cntr_lat, cntr_lon; //geographic coordinates Coords::rotatedToTrueLatLon(latitudeOfNorthernPole, longitudeOfNorthernPole, .5*(ll_latitude+ur_latitude), .5*(ll_longitude+ur_longitude), cntr_lat, cntr_lon); //determining cell size long Ni, Nj; GRIB_CHECK(grib_get_long(h,"Ni",&Ni),0); GRIB_CHECK(grib_get_long(h,"Nj",&Nj),0); double bearing; cell_x = Coords::VincentyDistance(cntr_lat, ll_lon, cntr_lat, ur_lon, bearing) / (double)Ni; cell_y = Coords::VincentyDistance(ll_lat, cntr_lon, ur_lat, cntr_lon, bearing) / (double)Nj; //determining bearing offset double delta_lat, delta_lon; //geographic coordinates Coords::rotatedToTrueLatLon(latitudeOfNorthernPole, longitudeOfNorthernPole, .5*(ll_latitude+ur_latitude)+1., .5*(ll_longitude+ur_longitude), delta_lat, delta_lon); Coords::VincentyDistance(cntr_lat, cntr_lon, delta_lat, delta_lon, bearing_offset); bearing_offset = fmod( bearing_offset + 180., 360.) - 180.; // turn into [-180;180) //returning the center point as reference Coords cntr(coordin, coordinparam); cntr.setLatLon(cntr_lat, cntr_lon, IOUtils::nodata); return cntr; } void GRIBIO::read2Dlevel(grib_handle* h, Grid2DObject& grid_out) { long Ni, Nj; GRIB_CHECK(grib_get_long(h,"Ni",&Ni),0); GRIB_CHECK(grib_get_long(h,"Nj",&Nj),0); size_t values_len= 0; GRIB_CHECK(grib_get_size(h,"values",&values_len),0); if(values_len!=(unsigned)(Ni*Nj)) { ostringstream ss; ss << "Declaring grid of size " << Ni << "x" << Nj << "=" << Ni*Nj << " "; ss << "but containing " << values_len << " values. This is inconsistent!"; throw InvalidArgumentException(ss.str(), AT); } double *values = (double*)calloc(values_len, sizeof(double)); GRIB_CHECK(grib_get_double_array(h,"values",values,&values_len),0); if (!llcorner_initialized) { //most important: get cellsize. llcorner will be finalized AFTER aspect ration correction double cellsize_x, cellsize_y; llcorner = getGeolocalization(h, cellsize_x, cellsize_y); //this is the center cell cellsize = (double)Optim::round( std::min(cellsize_x, cellsize_y) * 100. ) / 100.; // round to 1cm precision for numerical stability if( fabs(cellsize_x-cellsize_y)/cellsize_x > 1./100.) { factor_x = cellsize_x / cellsize; factor_y = cellsize_y / cellsize; } } grid_out.set(static_cast(Ni), static_cast(Nj), cellsize, llcorner); size_t i=0; for(size_t jj=0; jj<(unsigned)Nj; jj++) { for(size_t ii=0; ii<(unsigned)Ni; ii++) grid_out(ii,jj) = values[i++]; } free(values); //cells were not square, we have to resample if (factor_x!=IOUtils::nodata && factor_y!=IOUtils::nodata) { grid_out.grid2D = ResamplingAlgorithms2D::BilinearResampling(grid_out.grid2D, factor_x, factor_y); } if (!llcorner_initialized) { //take into account aspect ration conversion for computing true llcorner llcorner.moveByXY(-.5*(double)grid_out.getNx()*cellsize, -.5*(double)grid_out.getNy()*cellsize ); llcorner_initialized = true; grid_out.llcorner = llcorner; } } bool GRIBIO::read2DGrid_indexed(const double& in_marsParam, const long& i_levelType, const long& i_level, const Date i_date, Grid2DObject& grid_out) { GRIB_CHECK(grib_index_select_double(idx,"marsParam",in_marsParam),0); GRIB_CHECK(grib_index_select_long(idx,"indicatorOfTypeOfLevel", i_levelType),0); grib_handle* h=NULL; int err=0; while((h = grib_handle_new_from_index(idx,&err)) != NULL) { if(!h) { cleanup(); throw IOException("Unable to create grib handle from index", AT); } Date base_date; double P1, P2; getDate(h, base_date, P1, P2); //see WMO code table5 for definitions of timeRangeIndicator. http://dss.ucar.edu/docs/formats/grib/gribdoc/timer.html long timeRange; GRIB_CHECK(grib_get_long(h,"timeRangeIndicator", &timeRange),0); long level=0; if(i_level!=0) GRIB_CHECK(grib_get_long(h,"level", &level),0); if(level==i_level) { if( (i_date.isUndef()) || (timeRange==0 && i_date==base_date+P1) || (timeRange==1 && i_date==base_date) || ((timeRange==2 || timeRange==3) && i_date>=base_date+P1 && i_date<=base_date+P2) || ((timeRange==4 || timeRange==5) && i_date==base_date+P2) ) { read2Dlevel(h, grid_out); return true; } } grib_handle_delete(h); } return false; } void GRIBIO::read2DGrid(Grid2DObject& grid_out, const std::string& i_name) { const std::string filename = grid2dpath_in+"/"+i_name; fp = fopen(filename.c_str(),"r"); if(fp==NULL) { ostringstream ss; ss << "Error opening file \"" << filename << "\", possible reason: " << strerror(errno); throw FileAccessException(ss.str(), AT); } grib_handle* h=NULL; int err=0; if((h = grib_handle_new_from_file(0,fp,&err)) != NULL) { if(!h) { cleanup(); throw IOException("Unable to create grib handle for \""+filename+"\"", AT); } read2Dlevel(h, grid_out); grib_handle_delete(h); } else { cleanup(); throw IOException("No grid found in file \""+filename+"\"", AT); } cleanup(); } void GRIBIO::indexFile(const std::string& filename) { fp = fopen(filename.c_str(),"r"); if(fp==NULL) { ostringstream ss; ss << "Error opening file \"" << filename << "\", possible reason: " << strerror(errno); throw FileAccessException(ss.str(), AT); } int err=0; const std::string keys("marsParam:d,indicatorOfTypeOfLevel:l"); //indexing keys char *c_filename = (char *)filename.c_str(); idx = grib_index_new_from_file(0, c_filename, keys.c_str(), &err); if(err!=0) { cleanup(); throw IOException("Failed to index GRIB file \""+filename+"\". Is it a valid GRIB file?", AT); } indexed=true; idx_filename=filename; //read geolocalization of the first grid we find grib_handle* h=NULL; err=0; if((h = grib_handle_new_from_file(0,fp,&err)) != NULL) { if(!h) { cleanup(); throw IOException("Unable to create grib handle for \""+filename+"\"", AT); } grib_handle_delete(h); } else { cleanup(); throw IOException("No grid found in file \""+filename+"\"", AT); } } void GRIBIO::read2DGrid(Grid2DObject& grid_out, const MeteoGrids::Parameters& parameter, const Date& date) { Date UTC_date = date; UTC_date.setTimeZone(tz_in); const std::string filename = grid2dpath_in+"/"+grid2d_prefix+UTC_date.toString(Date::NUM).substr(0,10)+grid2d_ext; read2DGrid(filename, grid_out, parameter, UTC_date); } void GRIBIO::readWind(const std::string& filename, const Date& date) { if(wind_date==date) return; //wind fields are already up to date if(read2DGrid_indexed(32.2, 105, 10, date, VW)) { //FF_10M if(!read2DGrid_indexed(31.2, 105, 10, date, DW)) //DD_10M throw NoAvailableDataException("Can not read wind direction in file \""+filename+"\"", AT); } else { Grid2DObject U,V; read2DGrid_indexed(33.2, 105, 10, date, U); //U_10M, also in 110, 10 as U read2DGrid_indexed(34.2, 105, 10, date, V); //V_10M, also in 110, 10 as V VW.set(U.getNx(), U.getNy(), U.cellsize, U.llcorner); DW.set(U.getNx(), U.getNy(), U.cellsize, U.llcorner); for(size_t jj=0; jj& /*vecStation*/) { //Nothing so far throw IOException("Nothing implemented here", AT); } void GRIBIO::scanMeteoPath() { std::list dirlist; IOUtils::readDirectory(meteopath_in, dirlist, meteo_ext); dirlist.sort(); //Check date in every filename and cache it std::list::const_iterator it = dirlist.begin(); while ((it != dirlist.end())) { const std::string& filename = *it; const std::string::size_type spos = filename.find_first_of("0123456789"); Date date; IOUtils::convertString(date, filename.substr(spos,10), tz_in); const std::pair tmp(date, filename); cache_meteo_files.push_back(tmp); it++; } } void GRIBIO::readMeteoData(const Date& dateStart, const Date& dateEnd, std::vector< std::vector >& vecMeteo, const size_t&) { if(!meteo_initialized) { readStations(vecPts); scanMeteoPath(); meteo_initialized=true; } vecMeteo.clear(); double *lats = (double*)malloc(vecPts.size()*sizeof(double)); double *lons = (double*)malloc(vecPts.size()*sizeof(double)); std::vector meta; //metadata for meteo time series bool meta_ok=false; //set to true once the metadata have been read //find index of first time step size_t idx_start; bool start_found=false; for (idx_start=0; idx_start0) idx_start--; //start with first element before dateStart (useful for resampling) try { for(size_t ii=idx_start; iidateEnd) break; const std::string filename = meteopath_in+"/"+cache_meteo_files[ii].second; if(!indexed || idx_filename!=filename) { cleanup(); indexFile(filename); //this will also read geolocalization } if(!meta_ok) { if(readMeteoMeta(vecPts, meta, lats, lons)==false) { //some points have been removed vecPts has been changed -> re-reading free(lats); free(lons); lats = (double*)malloc(vecPts.size()*sizeof(double)); lons = (double*)malloc(vecPts.size()*sizeof(double)); readMeteoMeta(vecPts, meta, lats, lons); } vecMeteo.insert(vecMeteo.begin(), vecPts.size(), std::vector()); //allocation for the vectors now that we know how many true stations we have meta_ok=true; } std::vector Meteo; readMeteoStep(meta, lats, lons, date, Meteo); for(size_t jj=0; jj& vecPoints, double *lats, double *lons) { //remove potential duplicates. Returns true if some have been removed const size_t npoints = vecPoints.size(); std::vector deletions; deletions.reserve(npoints); for(size_t ii=0; ii0; ii--) { const size_t index=deletions[ii-1]; vecPoints.erase(vecPoints.begin()+index); } if(!deletions.empty()) return true; return false; } bool GRIBIO::readMeteoMeta(std::vector& vecPoints, std::vector &stations, double *lats, double *lons) {//return true if the metadata have been read, false if it needs to be re-read (ie: some points were leading to duplicates -> vecPoints has been changed) stations.clear(); GRIB_CHECK(grib_index_select_double(idx,"marsParam",8.2),0); //This is the DEM GRIB_CHECK(grib_index_select_long(idx,"indicatorOfTypeOfLevel", 1),0); int err=0; grib_handle* h = grib_handle_new_from_index(idx,&err); if(h==NULL) { cleanup(); throw IOException("Can not find DEM grid in GRIB file!", AT); } const size_t npoints = vecPoints.size(); double latitudeOfSouthernPole, longitudeOfSouthernPole; GRIB_CHECK(grib_get_double(h,"latitudeOfSouthernPoleInDegrees",&latitudeOfSouthernPole),0); GRIB_CHECK(grib_get_double(h,"longitudeOfSouthernPoleInDegrees",&longitudeOfSouthernPole),0); latitudeOfNorthernPole = -latitudeOfSouthernPole; longitudeOfNorthernPole = longitudeOfSouthernPole+180.; long Ni; GRIB_CHECK(grib_get_long(h,"Ni",&Ni),0); //build GRIB local coordinates for the points for(size_t ii=0; ii=base_date+P1 && i_date<=base_date+P2) || ((timeRange==4 || timeRange==5) && i_date==base_date+P2) ) { double *outlats = (double*)malloc(npoints*sizeof(double)); double *outlons = (double*)malloc(npoints*sizeof(double)); double *distances = (double*)malloc(npoints*sizeof(double)); int *indexes = (int *)malloc(npoints*sizeof(int)); if(grib_nearest_find_multiple(h, 0, lats, lons, npoints, outlats, outlons, values, distances, indexes)!=0) { grib_handle_delete(h); cleanup(); throw IOException("Errro when searching for nearest points in DEM", AT); } free(outlats); free(outlons); free(distances); free(indexes); grib_handle_delete(h); return true; } } grib_handle_delete(h); } return false; } void GRIBIO::fillMeteo(double *values, const MeteoData::Parameters& param, const size_t& npoints, std::vector &Meteo) { for(size_t ii=0; ii &stations, double *lats, double *lons, const Date i_date, std::vector &Meteo) { const size_t npoints = stations.size(); for(size_t ii=0; ii >& /*vecMeteo*/, const std::string&) { //Nothing so far throw IOException("Nothing implemented here", AT); } void GRIBIO::readPOI(std::vector&) { //Nothing so far throw IOException("Nothing implemented here", AT); } void GRIBIO::write2DGrid(const Grid2DObject& /*grid_in*/, const std::string& /*name*/) { //Nothing so far throw IOException("Nothing implemented here", AT); } void GRIBIO::write2DGrid(const Grid2DObject& /*grid_in*/, const MeteoGrids::Parameters& /*parameter*/, const Date& /*date*/) { //Nothing so far throw IOException("Nothing implemented here", AT); } void GRIBIO::cleanup() throw() { if(fp!=NULL) fclose(fp); fp=NULL; if(idx!=NULL) grib_index_delete(idx); idx=NULL; idx_filename.clear(); } } //namespace