#ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_LIBGRIB_API #include "dmemory.h" #include "cdi.h" #include "cdi_int.h" #include "file.h" #include "gribapi_utilities.h" #include "stream_scan.h" #include "stream_grb.h" #include "stream_gribapi.h" #include "varscan.h" #include "datetime.h" #include "vlist.h" #include "calendar.h" #include "subtype.h" #include "cgribex.h" /* gribGetSize, gribRead, gribGetZip, GRIB1_LTYPE_99 */ #include "gribapi.h" #include extern int CDI_inventory_mode; static const var_tile_t dummy_tiles = { 0, -1, -1, -1, -1, -1 }; typedef struct { int param; int level1; int level2; int ltype; int tsteptype; size_t gridsize; char name[32]; VarScanKeys scanKeys; var_tile_t tiles; } compvar2_t; static int gribapiGetZaxisType(long editionNumber, int grib_ltype) { return (editionNumber <= 1) ? grib1ltypeToZaxisType(grib_ltype) : grib2ltypeToZaxisType(grib_ltype); } static int getTimeunits(const long unitsOfTime) { switch (unitsOfTime) { case 13: return TUNIT_SECOND; case 0: return TUNIT_MINUTE; case 1: return TUNIT_HOUR; case 10: return TUNIT_3HOURS; case 11: return TUNIT_6HOURS; case 12: return TUNIT_12HOURS; case 2: return TUNIT_DAY; } return TUNIT_HOUR; } static double timeunit_factor(const int tu1, const int tu2) { if (tu2 == TUNIT_HOUR) { switch (tu1) { case TUNIT_SECOND: return 3600; case TUNIT_MINUTE: return 60; case TUNIT_HOUR: return 1; case TUNIT_3HOURS: return 1./3; case TUNIT_6HOURS: return 1./6; case TUNIT_12HOURS: return 1./12; case TUNIT_DAY: return 1./24; } } return 1; } static int gribapiGetTimeUnits(grib_handle *gh) { long unitsOfTime = -1; grib_get_long(gh, "indicatorOfUnitOfTimeRange", &unitsOfTime); GRIB_CHECK(my_grib_set_long(gh, "stepUnits", unitsOfTime), 0); return getTimeunits(unitsOfTime); } static void gribapiGetSteps(grib_handle *gh, int timeunits, int *startStep, int *endStep) { long unitsOfTime; int status = grib_get_long(gh, "stepUnits", &unitsOfTime); const int timeunits2 = (status == 0) ? getTimeunits(unitsOfTime) : timeunits; //timeunits2 = gribapiGetTimeUnits(gh); long lpar; status = grib_get_long(gh, "forecastTime", &lpar); if ( status == 0 ) *startStep = (int) lpar; else { status = grib_get_long(gh, "startStep", &lpar); if ( status == 0 ) *startStep = (int) (((double)lpar * timeunit_factor(timeunits, timeunits2)) + 0.5); } *endStep = *startStep; status = grib_get_long(gh, "endStep", &lpar); if ( status == 0 ) *endStep = (int) (((double)lpar * timeunit_factor(timeunits, timeunits2)) + 0.5); // printf("%d %d %d %d %d %g\n", *startStep, *endStep, lpar, timeunits, timeunits2, timeunit_factor(timeunits, timeunits2)); } static void gribapiGetDataDateTime(grib_handle *gh, int64_t *datadate, int *datatime) { long date; GRIB_CHECK(grib_get_long(gh, "dataDate", &date), 0); *datadate = (int64_t) date; long hour, minute, second; GRIB_CHECK(grib_get_long(gh, "hour", &hour), 0); GRIB_CHECK(grib_get_long(gh, "minute", &minute), 0); GRIB_CHECK(grib_get_long(gh, "second", &second), 0); *datatime = cdiEncodeTime((int)hour, (int)minute, (int)second); } static void gribapiSetDataDateTime(grib_handle *gh, int64_t datadate, int datatime) { GRIB_CHECK(my_grib_set_long(gh, "dataDate", (long)datadate), 0); int hour, minute, second; cdiDecodeTime(datatime, &hour, &minute, &second); GRIB_CHECK(my_grib_set_long(gh, "hour", hour), 0); GRIB_CHECK(my_grib_set_long(gh, "minute", minute), 0); GRIB_CHECK(my_grib_set_long(gh, "second", second), 0); } static int gribapiGetTimeUnitFactor(const int timeUnits) { static bool lprint = true; switch (timeUnits) { case TUNIT_SECOND: return 1; case TUNIT_MINUTE: return 60; case TUNIT_HOUR: return 3600; case TUNIT_3HOURS: return 10800; case TUNIT_6HOURS: return 21600; case TUNIT_12HOURS: return 43200; case TUNIT_DAY: return 86400; default: if ( lprint ) { Warning("Time unit %d unsupported", timeUnits); lprint = false; } } return 0; } static void gribapiGetValidityDateTime(grib_handle *gh, int64_t *vdate, int *vtime, int64_t *sDate, int *sTime) { *sDate = 0; *sTime = 0; long sigofrtime = 3; if ( gribEditionNumber(gh) > 1 ) GRIB_CHECK(grib_get_long(gh, "significanceOfReferenceTime", &sigofrtime), 0); else GRIB_CHECK(grib_get_long(gh, "timeRangeIndicator", &sigofrtime), 0); if ( sigofrtime == 3 ) //XXX: This looks like a bug to me, because timeRangeIndicator == 3 does not seem to have the same meaning as significanceOfReferenceTime == 3. I would recommend replacing this condition with `if(!gribapiTimeIsFC())`. { gribapiGetDataDateTime(gh, vdate, vtime); } else { int64_t rdate; int rtime; gribapiGetDataDateTime(gh, &rdate, &rtime); const int timeUnits = gribapiGetTimeUnits(gh); int startStep = 0, endStep = 0; gribapiGetSteps(gh, timeUnits, &startStep, &endStep); { int ryear, rmonth, rday, rhour, rminute, rsecond; cdiDecodeDate(rdate, &ryear, &rmonth, &rday); cdiDecodeTime(rtime, &rhour, &rminute, &rsecond); if ( rday > 0 ) { extern int CGRIBEX_grib_calendar; int64_t julday; int secofday; encode_caldaysec(CGRIBEX_grib_calendar, ryear, rmonth, rday, rhour, rminute, rsecond, &julday, &secofday); int64_t time_unit_factor = gribapiGetTimeUnitFactor(timeUnits); //if (startStep > 0) { int64_t julday_x = julday; int secofday_x = secofday; julday_add_seconds(time_unit_factor * startStep, &julday_x, &secofday_x); decode_caldaysec(CGRIBEX_grib_calendar, julday_x, secofday_x, &ryear, &rmonth, &rday, &rhour, &rminute, &rsecond); *sDate = cdiEncodeDate(ryear, rmonth, rday); *sTime = cdiEncodeTime(rhour, rminute, 0); } julday_add_seconds(time_unit_factor * endStep, &julday, &secofday); decode_caldaysec(CGRIBEX_grib_calendar, julday, secofday, &ryear, &rmonth, &rday, &rhour, &rminute, &rsecond); } *vdate = cdiEncodeDate(ryear, rmonth, rday); *vtime = cdiEncodeTime(rhour, rminute, rsecond); } } } static void grib1GetLevel(grib_handle *gh, int *leveltype, int *lbounds, int *level1, int *level2) { *leveltype = 0; *lbounds = 0; *level1 = 0; *level2 = 0; long lpar; if ( !grib_get_long(gh, "indicatorOfTypeOfLevel", &lpar) ) //1 byte { *leveltype = (int) lpar; switch (*leveltype) { case GRIB1_LTYPE_SIGMA_LAYER: case GRIB1_LTYPE_HYBRID_LAYER: case GRIB1_LTYPE_LANDDEPTH_LAYER: { *lbounds = 1; break; } } if ( *lbounds ) { GRIB_CHECK(grib_get_long(gh, "topLevel", &lpar), 0); //1 byte if (lpar == GRIB_MISSING_LONG) lpar = 0; *level1 = (int)lpar; GRIB_CHECK(grib_get_long(gh, "bottomLevel", &lpar), 0); //1 byte if (lpar == GRIB_MISSING_LONG) lpar = 0; *level2 = (int)lpar; } else { double dlevel; GRIB_CHECK(grib_get_double(gh, "level", &dlevel), 0); //2 byte if ( *leveltype == GRIB1_LTYPE_ISOBARIC ) dlevel *= 100; if ( dlevel < -2.e9 || dlevel > 2.e9 ) dlevel = 0; if ( *leveltype == GRIB1_LTYPE_99 || *leveltype == GRIB1_LTYPE_ISOBARIC_PA ) *leveltype = GRIB1_LTYPE_ISOBARIC; *level1 = (int) dlevel; *level2 = 0; } } } static double grib2ScaleFactor(const long factor) { switch(factor) { case GRIB_MISSING_LONG: return 1; case -9: return 1000000000; case -8: return 100000000; case -7: return 10000000; case -6: return 1000000; case -5: return 100000; case -4: return 10000; case -3: return 1000; case -2: return 100; case -1: return 10; case 0: return 1; case 1: return 0.1; case 2: return 0.01; case 3: return 0.001; case 4: return 0.0001; case 5: return 0.00001; case 6: return 0.000001; case 7: return 0.0000001; case 8: return 0.00000001; case 9: return 0.000000001; default: return 0; } } static int calcLevel(int level_sf, long factor, long level) { double result = 0; if (level != GRIB_MISSING_LONG) result = (double)level*grib2ScaleFactor(factor); if (level_sf) result *= level_sf; return (int)result; } static void grib2GetLevel(grib_handle *gh, int *leveltype1, int *leveltype2, int *lbounds, int *level1, int *level2, int *level_sf, int *level_unit) { *leveltype1 = 0; *leveltype2 = -1; *lbounds = 0; *level1 = 0; *level2 = 0; *level_sf = 0; *level_unit = 0; long lpar; int status = grib_get_long(gh, "typeOfFirstFixedSurface", &lpar); //1 byte if ( status == 0 ) { *leveltype1 = (int) lpar; status = grib_get_long(gh, "typeOfSecondFixedSurface", &lpar); //1 byte /* FIXME: assert(lpar >= INT_MIN && lpar <= INT_MAX) */ if ( status == 0 ) *leveltype2 = (int)lpar; if ( *leveltype1 != 255 && *leveltype2 != 255 && *leveltype2 > 0 ) *lbounds = 1; switch (*leveltype1) { case GRIB2_LTYPE_REFERENCE: if(*leveltype2 == 1) *lbounds = 0; break; case GRIB2_LTYPE_LANDDEPTH: *level_sf = 1000; *level_unit = CDI_UNIT_M; break; case GRIB2_LTYPE_ISOBARIC: *level_sf = 1000; *level_unit = CDI_UNIT_PA; break; case GRIB2_LTYPE_SIGMA: *level_sf = 1000; *level_unit = 0; break; } long factor, llevel; GRIB_CHECK(grib_get_long(gh, "scaleFactorOfFirstFixedSurface", &factor), 0); //1 byte GRIB_CHECK(grib_get_long(gh, "scaledValueOfFirstFixedSurface", &llevel), 0); //4 byte *level1 = calcLevel(*level_sf, factor, llevel); if ( *lbounds ) { GRIB_CHECK(grib_get_long(gh, "scaleFactorOfSecondFixedSurface", &factor), 0); //1 byte GRIB_CHECK(grib_get_long(gh, "scaledValueOfSecondFixedSurface", &llevel), 0); //4 byte *level2 = calcLevel(*level_sf, factor, llevel); } } } static void gribGetLevel(grib_handle *gh, int* leveltype1, int* leveltype2, int* lbounds, int* level1, int* level2, int* level_sf, int* level_unit, var_tile_t* tiles) { if ( gribEditionNumber(gh) <= 1 ) { grib1GetLevel(gh, leveltype1, lbounds, level1, level2); *leveltype2 = -1; *level_sf = 0; *level_unit = 0; } else { grib2GetLevel(gh, leveltype1, leveltype2, lbounds, level1, level2, level_sf, level_unit); /* read in tiles attributes (if there are any) */ tiles->tileindex = (int)gribGetLongDefault(gh, cdiSubtypeAttributeName[SUBTYPE_ATT_TILEINDEX], 0); tiles->totalno_of_tileattr_pairs = (int)gribGetLongDefault(gh, cdiSubtypeAttributeName[SUBTYPE_ATT_TOTALNO_OF_TILEATTR_PAIRS], -1); tiles->tileClassification = (int)gribGetLongDefault(gh, cdiSubtypeAttributeName[SUBTYPE_ATT_TILE_CLASSIFICATION], -1); tiles->numberOfTiles = (int)gribGetLongDefault(gh, cdiSubtypeAttributeName[SUBTYPE_ATT_NUMBER_OF_TILES], -1); tiles->numberOfAttributes = (int)gribGetLongDefault(gh, cdiSubtypeAttributeName[SUBTYPE_ATT_NUMBER_OF_ATTR], -1); tiles->attribute = (int)gribGetLongDefault(gh, cdiSubtypeAttributeName[SUBTYPE_ATT_TILEATTRIBUTE], -1); } } static void gribapiGetString(grib_handle *gh, const char *key, char *string, size_t length) { string[0] = 0; int ret = grib_get_string(gh, key, string, &length); if (ret != 0) { fprintf(stderr, "grib_get_string(gh, \"%s\", ...) failed!\n", key); GRIB_CHECK(ret, 0); } if ( length == 8 && memcmp(string, "unknown", length) == 0 ) string[0] = 0; else if ( length == 2 && memcmp(string, "~", length) == 0 ) string[0] = 0; } static int gribapiGetEnsembleInfo(grib_handle *gh, long *typeOfEnsembleForecast, long *numberOfForecastsInEnsemble, long *perturbationNumber) { int status = 0; if (grib_get_long(gh, "typeOfEnsembleForecast", typeOfEnsembleForecast) == 0) { GRIB_CHECK(grib_get_long(gh, "numberOfForecastsInEnsemble", numberOfForecastsInEnsemble), 0); GRIB_CHECK(grib_get_long(gh, "perturbationNumber", perturbationNumber), 0); if (*perturbationNumber > 0) status = 1; } if (status == 0) { *typeOfEnsembleForecast = 0; *perturbationNumber = 0; *numberOfForecastsInEnsemble = 0; } return status; } static VarScanKeys gribapiGetScanKeys(grib_handle *gh) { VarScanKeys scanKeys; varScanKeysInit(&scanKeys); long typeOfEnsembleForecast = 0, numberOfForecastsInEnsemble = 0, perturbationNumber = 0; gribapiGetEnsembleInfo(gh, &typeOfEnsembleForecast, &numberOfForecastsInEnsemble, &perturbationNumber); scanKeys.perturbationNumber = (short)perturbationNumber; long typeOfGeneratingProcess = 0; if (grib_get_long(gh, "typeOfGeneratingProcess", &typeOfGeneratingProcess) == 0) scanKeys.typeOfGeneratingProcess = (short)typeOfGeneratingProcess; return scanKeys; } static void gribapiGetNameKeys(grib_handle *gh, int varID) { char string[CDI_MAX_NAME]; size_t vlen = CDI_MAX_NAME; gribapiGetString(gh, "name", string, vlen); // longname if (string[0]) varDefKeyString(varID, CDI_KEY_LONGNAME, string); gribapiGetString(gh, "units", string, vlen); if (string[0]) varDefKeyString(varID, CDI_KEY_UNITS, string); string[0] = 0; const int status = grib_get_string(gh, "cfName", string, &vlen); if ( status != 0 || vlen <= 1 || strncmp(string, "unknown", 7) == 0 ) string[0] = 0; if (string[0]) varDefKeyString(varID, CDI_KEY_STDNAME, string); } static void gribapiGetKeys(grib_handle *gh, int varID) { long tablesVersion = 0; if ( grib_get_long(gh, "tablesVersion", &tablesVersion) == 0 ) varDefKeyInt(varID, CDI_KEY_TABLESVERSION, (int) tablesVersion); long localTablesVersion = 0; if ( grib_get_long(gh, "localTablesVersion", &localTablesVersion) == 0 ) varDefKeyInt(varID, CDI_KEY_LOCALTABLESVERSION, (int) localTablesVersion); long typeOfGeneratingProcess = 0; if ( grib_get_long(gh, "typeOfGeneratingProcess", &typeOfGeneratingProcess) == 0 ) varDefKeyInt(varID, CDI_KEY_TYPEOFGENERATINGPROCESS, (int) typeOfGeneratingProcess); long productDefinitionTemplate = 0; if ( grib_get_long(gh, "productDefinitionTemplateNumber", &productDefinitionTemplate) == 0 ) varDefKeyInt(varID, CDI_KEY_PRODUCTDEFINITIONTEMPLATE, (int) productDefinitionTemplate); long typeOfProcessedData = 0; if ( grib_get_long(gh, "typeOfProcessedData", &typeOfProcessedData) == 0 ) varDefKeyInt(varID, CDI_KEY_TYPEOFPROCESSEDDATA, (int) typeOfProcessedData); long shapeOfTheEarth = 0; if ( grib_get_long(gh, "shapeOfTheEarth", &shapeOfTheEarth) == 0 ) varDefKeyInt(varID, CDI_KEY_SHAPEOFTHEEARTH, (int) shapeOfTheEarth); long backgroundProcess = 0; if ( grib_get_long(gh, "backgroundProcess", &backgroundProcess) == 0 ) varDefKeyInt(varID, CDI_KEY_BACKGROUNDPROCESS, (int) backgroundProcess); long typeOfTimeIncrement = 0; if ( grib_get_long(gh, "typeOfTimeIncrement", &typeOfTimeIncrement) == 0 ) varDefKeyInt(varID, CDI_KEY_TYPEOFTIMEINCREMENT, (int) typeOfTimeIncrement); /* long constituentType = 0; if ( grib_get_long(gh, "constituentType", &constituentType) == 0 ) varDefKeyInt(varID, CDI_KEY_CONSTITUENTTYPE, (int) constituentType); */ /* Get the ensemble Info from the grib-2 Tables and update the intermediate datastructure. Further update to the "vlist" is handled in the same way as for GRIB-1 by "cdi_generate_vars" */ long typeOfEnsembleForecast = 0, numberOfForecastsInEnsemble = 0, perturbationNumber = 0; gribapiGetEnsembleInfo(gh, &typeOfEnsembleForecast, &numberOfForecastsInEnsemble, &perturbationNumber); if ( perturbationNumber > 0 ) { varDefKeyInt(varID, CDI_KEY_TYPEOFENSEMBLEFORECAST, (int) typeOfEnsembleForecast); varDefKeyInt(varID, CDI_KEY_NUMBEROFFORECASTSINENSEMBLE, (int) numberOfForecastsInEnsemble); varDefKeyInt(varID, CDI_KEY_PERTURBATIONNUMBER, (int) perturbationNumber); } long section2Length = 0; int status = grib_get_long(gh, "section2Length", §ion2Length); if (status == 0 && section2Length > 0) { long grib2LocalSectionNumber; long mpimType, mpimClass, mpimUser; status = grib_get_long(gh, "grib2LocalSectionNumber", &grib2LocalSectionNumber); if (status == 0) { size_t section2PaddingLength = 0; status = grib_get_size(gh, "section2Padding", §ion2PaddingLength); if (status == 0 && section2PaddingLength > 0) { varDefKeyInt(varID, CDI_KEY_GRIB2LOCALSECTIONNUMBER, (int) grib2LocalSectionNumber); varDefKeyInt(varID, CDI_KEY_SECTION2PADDINGLENGTH, (int) section2PaddingLength); unsigned char *section2Padding = (unsigned char*) Malloc(section2PaddingLength); grib_get_bytes(gh, "section2Padding", section2Padding, §ion2PaddingLength); varDefKeyBytes(varID, CDI_KEY_SECTION2PADDING, section2Padding, (int)section2PaddingLength); Free(section2Padding); } else if ( grib_get_long(gh, "mpimType", &mpimType) == 0 && grib_get_long(gh, "mpimClass", &mpimClass) == 0 && grib_get_long(gh, "mpimUser", &mpimUser) == 0) { varDefKeyInt(varID, CDI_KEY_MPIMTYPE, mpimType); varDefKeyInt(varID, CDI_KEY_MPIMCLASS, mpimClass); varDefKeyInt(varID, CDI_KEY_MPIMUSER, mpimUser); size_t revNumLen = 20; unsigned char revNumber[revNumLen]; if ( grib_get_bytes(gh, "revNumber", revNumber, &revNumLen) == 0 ) varDefKeyBytes(varID, CDI_KEY_REVNUMBER, revNumber, (int)revNumLen); long revStatus; grib_get_long(gh, "revStatus", &revStatus); varDefKeyInt(varID, CDI_KEY_REVSTATUS, revStatus); } } } } static void gribapiDefProjRLL(grib_handle *gh, int gridID) { double xpole = 0, ypole = 0, angle = 0; grib_get_double(gh, "latitudeOfSouthernPoleInDegrees", &ypole); grib_get_double(gh, "longitudeOfSouthernPoleInDegrees", &xpole); grib_get_double(gh, "angleOfRotation", &angle); xpole -= 180; if ( fabs(ypole) > 0 ) ypole = -ypole; // change from south to north pole if ( fabs(angle) > 0 ) angle = -angle; gridDefParamRLL(gridID, xpole, ypole, angle); } static double shapeOfTheEarthToRadius(long shapeOfTheEarth) { switch (shapeOfTheEarth) { case 0: return 6367470.; case 2: return 6378160.; case 6: return 6371229.; case 8: return 6371200.; } return 6367470.; } static void gribapiDefProjLCC(grib_handle *gh, int gridID) { long shapeOfTheEarth; grib_get_long(gh, "shapeOfTheEarth", &shapeOfTheEarth); const double a = shapeOfTheEarthToRadius(shapeOfTheEarth); const double rf = (shapeOfTheEarth == 2) ? 297.0 : 0; double lon_0, lat_1, lat_2, xval_0, yval_0; long projflag = 0; grib_get_double(gh, "longitudeOfFirstGridPointInDegrees", &xval_0); grib_get_double(gh, "latitudeOfFirstGridPointInDegrees", &yval_0); grib_get_double(gh, "LoVInDegrees", &lon_0); grib_get_double(gh, "Latin1InDegrees", &lat_1); grib_get_double(gh, "Latin2InDegrees", &lat_2); grib_get_long(gh, "projectionCentreFlag", &projflag); bool lsouth = gribbyte_get_bit((int)projflag, 1); if ( lsouth ) { lat_1 = -lat_1; lat_2 = -lat_2; } double lat_0 = lat_2; double x_0 = CDI_grid_missval; double y_0 = CDI_grid_missval; if ( proj_lonlat_to_lcc_func ) { x_0 = xval_0; y_0 = yval_0; proj_lonlat_to_lcc_func(CDI_grid_missval, lon_0, lat_0, lat_1, lat_2, a, rf, (size_t)1, &x_0, &y_0); if ( IS_NOT_EQUAL(x_0, CDI_grid_missval) && IS_NOT_EQUAL(y_0, CDI_grid_missval) ) { x_0 = -x_0; y_0 = -y_0; } } gridDefParamLCC(gridID, CDI_grid_missval, lon_0, lat_0, lat_1, lat_2, a, rf, xval_0, yval_0, x_0, y_0); } static void gribapiDefProjSTERE(grib_handle *gh, int gridID) { long shapeOfTheEarth; grib_get_long(gh, "shapeOfTheEarth", &shapeOfTheEarth); const double a = shapeOfTheEarthToRadius(shapeOfTheEarth); double lon_0, lat_ts, xval_0, yval_0; grib_get_double(gh, "longitudeOfFirstGridPointInDegrees", &xval_0); grib_get_double(gh, "latitudeOfFirstGridPointInDegrees", &yval_0); grib_get_double(gh, "LaDInDegrees", &lat_ts); grib_get_double(gh, "orientationOfTheGridInDegrees", &lon_0); long southPoleOnProjectionPlane; grib_get_long(gh, "southPoleOnProjectionPlane", &southPoleOnProjectionPlane); double lat_0 = southPoleOnProjectionPlane ? -90 : 90; double x_0 = CDI_grid_missval; double y_0 = CDI_grid_missval; if ( proj_lonlat_to_stere_func ) { x_0 = xval_0; y_0 = yval_0; proj_lonlat_to_stere_func(CDI_grid_missval, lon_0, lat_ts, lat_0, a, (size_t)1, &x_0, &y_0); if ( IS_NOT_EQUAL(x_0, CDI_grid_missval) && IS_NOT_EQUAL(y_0, CDI_grid_missval) ) { x_0 = -x_0; y_0 = -y_0; } } gridDefParamSTERE(gridID, CDI_grid_missval, lon_0, lat_ts, lat_0, a, xval_0, yval_0, x_0, y_0); } static void gribapiAddRecord(stream_t *streamptr, int param, grib_handle *gh, size_t recsize, off_t position, int datatype, int comptype, const char *varname, int leveltype1, int leveltype2, int lbounds, int level1, int level2, int level_sf, int level_unit, VarScanKeys *scanKeys, const var_tile_t *tiles, int lread_additional_keys) { const int vlistID = streamptr->vlistID; const int tsID = streamptr->curTsID; const int recID = recordNewEntry(streamptr, tsID); record_t *record = &streamptr->tsteps[tsID].records[recID]; const int tsteptype = gribapiGetTsteptype(gh); // numavg = ISEC1_AvgNum; int numavg = 0; // fprintf(stderr, "param %d %d %d %d\n", param, level1, level2, leveltype1); record->size = recsize; record->position = position; record->param = param; record->ilevel = level1; record->ilevel2 = level2; record->ltype = leveltype1; record->tsteptype = (short)tsteptype; record->gridsize = gribapiGetGridsize(gh); record->scanKeys = *scanKeys; record->tiles = tiles ? *tiles : dummy_tiles; strncpy(record->varname, varname, sizeof(record->varname)-1); record->varname[sizeof(record->varname) - 1] = 0; grid_t *grid = (grid_t *)Malloc(sizeof(*grid)); const bool uvRelativeToGrid = gribapiGetGrid(gh, grid); struct addIfNewRes gridAdded = cdiVlistAddGridIfNew(vlistID, grid, 0); const int gridID = gridAdded.Id; if ( !gridAdded.isNew ) Free(grid); else if ( grid->projtype == CDI_PROJ_RLL ) gribapiDefProjRLL(gh, gridID); else if ( grid->projtype == CDI_PROJ_LCC ) gribapiDefProjLCC(gh, gridID); else if ( grid->projtype == CDI_PROJ_STERE ) gribapiDefProjSTERE(gh, gridID); const int zaxistype = gribapiGetZaxisType(gribEditionNumber(gh), leveltype1); switch (zaxistype) { case ZAXIS_HYBRID: case ZAXIS_HYBRID_HALF: { long lpar; GRIB_CHECK(grib_get_long(gh, "NV", &lpar), 0); /* FIXME: assert(lpar >= 0) */ const size_t vctsize = (size_t)lpar; if ( vctsize > 0 ) { double *vctptr = (double *) Malloc(vctsize*sizeof(double)); size_t dummy = vctsize; GRIB_CHECK(grib_get_double_array(gh, "pv", vctptr, &dummy), 0); varDefVCT(vctsize, vctptr); Free(vctptr); } break; } case ZAXIS_REFERENCE: { unsigned char uuid[CDI_UUID_SIZE]; long lpar; GRIB_CHECK(grib_get_long(gh, "NV", &lpar), 0); if ( lpar != 6 ) fprintf(stderr, "Warning ...\n"); GRIB_CHECK(grib_get_long(gh, "nlev", &lpar), 0); int nhlev = (int)lpar; GRIB_CHECK(grib_get_long(gh, "numberOfVGridUsed", &lpar), 0); int nvgrid = (int)lpar; size_t len = (size_t)CDI_UUID_SIZE; memset(uuid, 0, CDI_UUID_SIZE); GRIB_CHECK(grib_get_bytes(gh, "uuidOfVGrid", uuid, &len), 0); varDefZAxisReference(nhlev, nvgrid, uuid); break; } } // if ( datatype > 32 ) datatype = CDI_DATATYPE_PACK32; if ( datatype < 0 ) datatype = CDI_DATATYPE_PACK; // add the previously read record data to the (intermediate) list of records int tile_index = 0; int varID = 0, levelID = 0; varAddRecord(recID, param, gridID, zaxistype, lbounds, level1, level2, level_sf, level_unit, datatype, &varID, &levelID, tsteptype, numavg, leveltype1, leveltype2, varname, scanKeys, tiles, &tile_index); record->varID = (short)varID; record->levelID = (short)levelID; varDefCompType(varID, comptype); if ( uvRelativeToGrid ) varDefKeyInt(varID, CDI_KEY_UVRELATIVETOGRID, 1); if (varname[0]) gribapiGetNameKeys(gh, varID); gribapiGetKeys(gh, varID); if (lread_additional_keys) { long lval; double dval; for ( int i = 0; i < cdiNAdditionalGRIBKeys; i++ ) { // note: if the key is not defined, we do not throw an error! if ( grib_get_long(gh, cdiAdditionalGRIBKeys[i], &lval) == 0 ) varDefOptGribInt(varID, tile_index, lval, cdiAdditionalGRIBKeys[i]); if ( grib_get_double(gh, cdiAdditionalGRIBKeys[i], &dval) == 0 ) varDefOptGribDbl(varID, tile_index, dval, cdiAdditionalGRIBKeys[i]); } } if ( varInqInst(varID) == CDI_UNDEFID ) { long center, subcenter; GRIB_CHECK(grib_get_long(gh, "centre", ¢er), 0); GRIB_CHECK(grib_get_long(gh, "subCentre", &subcenter), 0); int instID = institutInq((int)center, (int)subcenter, NULL, NULL); if ( instID == CDI_UNDEFID ) instID = institutDef((int)center, (int)subcenter, NULL, NULL); varDefInst(varID, instID); } if ( varInqModel(varID) == CDI_UNDEFID ) { long processID; if ( grib_get_long(gh, "generatingProcessIdentifier", &processID) == 0 ) { /* FIXME: assert(processID >= INT_MIN && processID <= INT_MAX) */ int modelID = modelInq(varInqInst(varID), (int)processID, NULL); if ( modelID == CDI_UNDEFID ) modelID = modelDef(varInqInst(varID), (int)processID, NULL); varDefModel(varID, modelID); } } if ( varInqTable(varID) == CDI_UNDEFID ) { int pdis, pcat, pnum; cdiDecodeParam(param, &pnum, &pcat, &pdis); if ( pdis == 255 ) { int tabnum = pcat; int tableID = tableInq(varInqModel(varID), tabnum, NULL); if ( tableID == CDI_UNDEFID ) tableID = tableDef(varInqModel(varID), tabnum, NULL); varDefTable(varID, tableID); } } streamptr->tsteps[tsID].nallrecs++; streamptr->nrecs++; if ( CDI_Debug ) Message("varID = %d param = %d zaxistype = %d gridID = %d levelID = %d", varID, param, zaxistype, gridID, levelID); } static compvar2_t gribapiVarSet(int param, int level1, int level2, int leveltype, int tsteptype, size_t gridsize, char *name, VarScanKeys scanKeys, var_tile_t tiles_data) { compvar2_t compVar; memset(&compVar, 0, sizeof(compvar2_t)); const size_t maxlen = sizeof(compVar.name); size_t len = strlen(name); if ( len > maxlen ) len = maxlen; compVar.param = param; compVar.level1 = level1; compVar.level2 = level2; compVar.ltype = leveltype; compVar.tsteptype = tsteptype; compVar.gridsize = gridsize; //memset(compVar.name, 0, maxlen); memcpy(compVar.name, name, len); compVar.scanKeys = scanKeys; compVar.tiles = tiles_data; return compVar; } static int gribapiVarCompare(compvar2_t compVar, record_t record, int flag) { compvar2_t compVar0; memset(&compVar0, 0, sizeof(compvar2_t)); compVar0.param = record.param; compVar0.level1 = record.ilevel; compVar0.level2 = record.ilevel2; compVar0.ltype = record.ltype; compVar0.tsteptype = record.tsteptype; compVar0.gridsize = record.gridsize; memcpy(compVar0.name, record.varname, sizeof(compVar.name)); if ( flag == 0 ) { if ( compVar0.tsteptype == TSTEP_INSTANT && compVar.tsteptype == TSTEP_INSTANT3 ) compVar0.tsteptype = TSTEP_INSTANT3; if ( compVar0.tsteptype == TSTEP_INSTANT3 && compVar.tsteptype == TSTEP_INSTANT ) compVar0.tsteptype = TSTEP_INSTANT; } compVar0.scanKeys = record.scanKeys; compVar0.tiles = record.tiles; return memcmp(&compVar0, &compVar, sizeof(compvar2_t)); } static grib_handle *gribapiGetDiskRepresentation(size_t recsize, size_t *buffersize, void **gribbuffer, int *outDatatype, int *outCompressionType) { const int gribversion = (int)((char*)*gribbuffer)[7]; if ( gribversion <= 1 ) *outCompressionType = grbDecompress(recsize, buffersize, gribbuffer); grib_handle *gh = grib_handle_new_from_message(NULL, *gribbuffer, recsize); bool lieee = false; if ( gribversion > 1 ) { size_t len = 256; char typeOfPacking[256]; if ( grib_get_string(gh, "packingType", typeOfPacking, &len) == 0 ) { // fprintf(stderr, "packingType %d %s\n", len, typeOfPacking); if ( strncmp(typeOfPacking, "grid_jpeg", len) == 0 ) *outCompressionType = CDI_COMPRESS_JPEG; else if ( strncmp(typeOfPacking, "grid_ccsds", len) == 0 ) *outCompressionType = CDI_COMPRESS_AEC; else if ( strncmp(typeOfPacking, "grid_ieee", len) == 0 ) lieee = true; } } if ( lieee ) { *outDatatype = CDI_DATATYPE_FLT64; long precision; const int status = grib_get_long(gh, "precision", &precision); if ( status == 0 && precision == 1 ) *outDatatype = CDI_DATATYPE_FLT32; } else { *outDatatype = CDI_DATATYPE_PACK; long bitsPerValue; if ( grib_get_long(gh, "bitsPerValue", &bitsPerValue) == 0 ) { if ( bitsPerValue > 0 && bitsPerValue <= 32 ) *outDatatype = (int)bitsPerValue; } } return gh; } typedef enum { CHECKTIME_OK, CHECKTIME_SKIP, CHECKTIME_STOP, CHECKTIME_INCONSISTENT } checkTimeResult; static checkTimeResult checkTime(stream_t* streamptr, compvar2_t compVar, const DateTime* verificationTime, const DateTime* expectedVTime) { // First determine whether the current record exists already. int recID = 0; for ( ; recID < streamptr->nrecs; recID++ ) { if ( gribapiVarCompare(compVar, streamptr->tsteps[0].records[recID], 1) == 0 ) break; } const bool recordExists = recID < streamptr->nrecs; // Then we need to know whether the verification time is consistent. const bool consistentTime = !memcmp(verificationTime, expectedVTime, sizeof(*verificationTime)); // Finally, we make a decision. if ( CDI_inventory_mode == 1 ) { if ( recordExists ) return CHECKTIME_STOP; if ( !consistentTime ) return CHECKTIME_INCONSISTENT; } else { if ( !consistentTime ) return CHECKTIME_STOP; if ( recordExists ) return CHECKTIME_SKIP; } return CHECKTIME_OK; } #define gribWarning(text, nrecs, timestep, varname, param, level1, level2) do \ { \ char paramstr[32]; \ cdiParamToString(param, paramstr, sizeof(paramstr)); \ Warning("Record %2d (name=%s id=%s lev1=%d lev2=%d) timestep %d: %s", nrecs, varname, paramstr, level1, level2, timestep, text); \ } \ while(0) int gribapiScanTimestep1(stream_t * streamptr) { off_t recpos = 0; void *gribbuffer = NULL; size_t buffersize = 0; DateTime datetime0 = { .date = 10101, .time = 0 }; int nrecs_scanned = 0; //Only used for debug output. bool warn_time = true; // bool warn_numavg = true; int fcast = 0; grib_handle *gh = NULL; streamptr->curTsID = 0; const int tsID = tstepsNewEntry(streamptr); if ( tsID != 0 ) Error("Internal problem! tstepsNewEntry returns %d", tsID); taxis_t *taxis = &streamptr->tsteps[tsID].taxis; const int fileID = streamptr->fileID; unsigned nrecs = 0; while ( true ) { int level1 = 0, level2 = 0; const size_t recsize = gribGetSize(fileID); recpos = fileGetPos(fileID); if ( recsize == 0 ) { streamptr->ntsteps = 1; break; } ensureBufferSize(recsize, &buffersize, &gribbuffer); size_t readsize = recsize; // Search for next 'GRIB', read the following record, and position file offset after it. if (gribRead(fileID, gribbuffer, &readsize)) break; int datatype, comptype = 0; gh = gribapiGetDiskRepresentation(recsize, &buffersize, &gribbuffer, &datatype, &comptype); nrecs_scanned++; GRIB_CHECK(my_grib_set_double(gh, "missingValue", CDI_default_missval), 0); const int param = gribapiGetParam(gh); int leveltype1 = -1, leveltype2 = -1, lbounds, level_sf, level_unit; var_tile_t tiles = dummy_tiles; gribGetLevel(gh, &leveltype1, &leveltype2, &lbounds, &level1, &level2, &level_sf, &level_unit, &tiles); char varname[256]; gribapiGetString(gh, "shortName", varname, sizeof(varname)); int64_t vdate, sdate; int vtime, stime; gribapiGetValidityDateTime(gh, &vdate, &vtime, &sdate, &stime); DateTime datetime = { .date = vdate, .time = vtime }; VarScanKeys scanKeys = gribapiGetScanKeys(gh); if ( nrecs == 0 ) { datetime0 = datetime; gribapiGetDataDateTime(gh, &(taxis->rdate), &(taxis->rtime)); fcast = gribapiTimeIsFC(gh); if ( fcast ) taxis->unit = gribapiGetTimeUnits(gh); taxis->fdate = taxis->rdate; taxis->ftime = taxis->rtime; taxis->sdate = sdate; taxis->stime = stime; taxis->vdate = vdate; taxis->vtime = vtime; } else { const int tsteptype = gribapiGetTsteptype(gh); const size_t gridsize = gribapiGetGridsize(gh); checkTimeResult result = checkTime(streamptr, gribapiVarSet(param, level1, level2, leveltype1, tsteptype, gridsize, varname, scanKeys, tiles), &datetime, &datetime0); if ( result == CHECKTIME_STOP ) { break; } else if ( result == CHECKTIME_SKIP ) { gribWarning("Parameter already exist, skipped!", nrecs_scanned, tsID+1, varname, param, level1, level2); continue; } else if ( result == CHECKTIME_INCONSISTENT && warn_time ) { gribWarning("Inconsistent verification time!", nrecs_scanned, tsID+1, varname, param, level1, level2); warn_time = false; } assert(result == CHECKTIME_OK || result == CHECKTIME_INCONSISTENT); } nrecs++; if ( CDI_Debug ) { char paramstr[32]; cdiParamToString(param, paramstr, sizeof(paramstr)); Message("%4u %8d name=%s id=%s ltype=%d lev1=%d lev2=%d vdate=%lld vtime=%d", nrecs, (int)recpos, varname, paramstr, leveltype1, level1, level2, vdate, vtime); } var_tile_t *ptiles = memcmp(&tiles, &dummy_tiles, sizeof(var_tile_t)) ? &tiles : NULL; gribapiAddRecord(streamptr, param, gh, recsize, recpos, datatype, comptype, varname, leveltype1, leveltype2, lbounds, level1, level2, level_sf, level_unit, &scanKeys, ptiles, 1); grib_handle_delete(gh); gh = NULL; } if ( gh ) grib_handle_delete(gh); streamptr->rtsteps = 1; if ( nrecs == 0 ) return CDI_EUFSTRUCT; cdi_generate_vars(streamptr); taxis->type = fcast ? TAXIS_RELATIVE : TAXIS_ABSOLUTE; int taxisID = taxisCreate(taxis->type); // printf("1: %d %6d %d %6d %d %6d\n", taxis->rdate, taxis->rtime, taxis->sdate, taxis->stime, taxis->vdate, taxis->vtime); const int vlistID = streamptr->vlistID; vlistDefTaxis(vlistID, taxisID); streamScanResizeRecords1(streamptr); streamptr->record->buffer = gribbuffer; streamptr->record->buffersize = buffersize; streamScanTsFixNtsteps(streamptr, recpos); streamScanTimeConstAdjust(streamptr, taxis); return 0; } int gribapiScanTimestep2(stream_t * streamptr) { int rstatus = 0; off_t recpos = 0; DateTime datetime0 = { LONG_MIN, LONG_MIN }; // int gridID; int recID; // bool warn_numavg = true; grib_handle *gh = NULL; streamptr->curTsID = 1; const int fileID = streamptr->fileID; const int vlistID = streamptr->vlistID; void *gribbuffer = streamptr->record->buffer; size_t buffersize = streamptr->record->buffersize; int tsID = streamptr->rtsteps; if ( tsID != 1 ) Error("Internal problem! unexpected timestep %d", tsID+1); taxis_t *taxis = &streamptr->tsteps[tsID].taxis; fileSetPos(fileID, streamptr->tsteps[tsID].position, SEEK_SET); cdi_create_records(streamptr, tsID); record_t *records = streamptr->tsteps[tsID].records; const int nrecords = streamScanInitRecords2(streamptr); int nrecs_scanned = nrecords; //Only used for debug output int rindex = 0; while ( true ) { if ( rindex > nrecords ) break; const size_t recsize = gribGetSize(fileID); recpos = fileGetPos(fileID); if ( recsize == 0 ) { streamptr->ntsteps = 2; break; } ensureBufferSize(recsize, &buffersize, &gribbuffer); size_t readsize = recsize; if (gribRead(fileID, gribbuffer, &readsize)) break; grbDecompress(recsize, &buffersize, &gribbuffer); nrecs_scanned++; gh = grib_handle_new_from_message(NULL, gribbuffer, recsize); GRIB_CHECK(my_grib_set_double(gh, "missingValue", CDI_default_missval), 0); const int param = gribapiGetParam(gh); int level1 = 0, level2 = 0, leveltype1, leveltype2, lbounds, level_sf, level_unit; var_tile_t tiles = dummy_tiles; gribGetLevel(gh, &leveltype1, &leveltype2, &lbounds, &level1, &level2, &level_sf, &level_unit, &tiles); char varname[256]; gribapiGetString(gh, "shortName", varname, sizeof(varname)); int64_t vdate, sdate; int vtime, stime; gribapiGetValidityDateTime(gh, &vdate, &vtime, &sdate, &stime); DateTime datetime = { .date = vdate, .time = vtime }; if ( rindex == 0 ) { datetime0 = datetime; const int taxisID = vlistInqTaxis(vlistID); if ( taxisInqType(taxisID) == TAXIS_RELATIVE ) { taxis->type = TAXIS_RELATIVE; taxis->unit = gribapiGetTimeUnits(gh); gribapiGetDataDateTime(gh, &(taxis->rdate), &(taxis->rtime)); } else { taxis->type = TAXIS_ABSOLUTE; } taxis->fdate = taxis->rdate; taxis->ftime = taxis->rtime; taxis->vdate = vdate; taxis->vtime = vtime; taxis->sdate = sdate; taxis->stime = stime; // printf("2: %d %6d %d %6d %d %6d\n", taxis->rdate, taxis->rtime, taxis->sdate, taxis->stime, taxis->vdate, taxis->vtime); } VarScanKeys scanKeys = gribapiGetScanKeys(gh); const int tsteptype = gribapiGetTsteptype(gh); const size_t gridsize = gribapiGetGridsize(gh); compvar2_t compVar = gribapiVarSet(param, level1, level2, leveltype1, tsteptype, gridsize, varname, scanKeys, tiles); for ( recID = 0; recID < nrecords; recID++ ) if ( gribapiVarCompare(compVar, records[recID], 0) == 0 ) break; if ( recID == nrecords ) { if ( CDI_inventory_mode == 1 ) { gribWarning("Parameter not defined at timestep 1!", nrecs_scanned, tsID+1, varname, param, level1, level2); return CDI_EUFSTRUCT; } else { gribWarning("Parameter not defined at timestep 1, skipped!", nrecs_scanned, tsID+1, varname, param, level1, level2); continue; } } if ( records[recID].used ) { if ( CDI_inventory_mode == 1 ) break; else { if ( datetimeDiffer(datetime, datetime0) ) break; gribWarning("Parameter already exist, skipped!", nrecs_scanned, tsID+1, varname, param, level1, level2); continue; } } records[recID].used = true; streamptr->tsteps[tsID].recIDs[rindex] = recID; if ( CDI_Debug ) { char paramstr[32]; cdiParamToString(param, paramstr, sizeof(paramstr)); Message("%4d %8d name=%s id=%s ltype=%d lev1=%d lev2=%d vdate=%lld vtime=%d", nrecs_scanned, (int)recpos, varname, paramstr, leveltype1, level1, level2, vdate, vtime); } if ( gribapiVarCompare(compVar, records[recID], 0) != 0 ) { Message("tsID = %d recID = %d param = %3d new %3d level = %3d new %3d", tsID, recID, records[recID].param, param, records[recID].ilevel, level1); return CDI_EUFSTRUCT; } records[recID].position = recpos; records[recID].size = recsize; const int varID = records[recID].varID; if ( tsteptype != vlistInqVarTsteptype(vlistID, varID) ) vlistDefVarTsteptype(vlistID, varID, tsteptype); grib_handle_delete(gh); gh = NULL; rindex++; } if ( gh ) grib_handle_delete(gh); int nrecs = 0; for ( recID = 0; recID < nrecords; recID++ ) { if ( ! records[recID].used ) { vlistDefVarTimetype(vlistID, records[recID].varID, TIME_CONSTANT); } else { nrecs++; } } streamptr->tsteps[tsID].nrecs = nrecs; streamptr->rtsteps = 2; streamScanTsFixNtsteps(streamptr, recpos); streamptr->record->buffer = gribbuffer; streamptr->record->buffersize = buffersize; return rstatus; } int gribapiScanTimestep(stream_t * streamptr) { int vrecID, recID = -1; //bool warn_numavg = true; int nrecs = 0; int vlistID = streamptr->vlistID; int tsID = streamptr->rtsteps; taxis_t *taxis = &streamptr->tsteps[tsID].taxis; if ( streamptr->tsteps[tsID].recordSize == 0 ) { void *gribbuffer = streamptr->record->buffer; size_t buffersize = streamptr->record->buffersize; cdi_create_records(streamptr, tsID); record_t *records = streamptr->tsteps[tsID].records; nrecs = streamScanInitRecords(streamptr, tsID); const int fileID = streamptr->fileID; fileSetPos(fileID, streamptr->tsteps[tsID].position, SEEK_SET); int nrecs_scanned = streamptr->tsteps[0].nallrecs + streamptr->tsteps[1].nrecs*(tsID-1); //Only used for debug output. int rindex = 0; off_t recpos = 0; DateTime datetime0 = { LONG_MIN, LONG_MIN }; grib_handle *gh = NULL; char varname[256]; while ( true ) { if ( rindex > nrecs ) break; const size_t recsize = gribGetSize(fileID); recpos = fileGetPos(fileID); if ( recsize == 0 ) { streamptr->ntsteps = streamptr->rtsteps + 1; break; } if ( rindex >= nrecs ) break; ensureBufferSize(recsize, &buffersize, &gribbuffer); size_t readsize = recsize; if (gribRead(fileID, gribbuffer, &readsize)) { Warning("Inconsistent timestep %d (GRIB record %d/%d)!", tsID+1, rindex+1, streamptr->tsteps[tsID].recordSize); break; } grbDecompress(recsize, &buffersize, &gribbuffer); nrecs_scanned++; gh = grib_handle_new_from_message(NULL, gribbuffer, recsize); GRIB_CHECK(my_grib_set_double(gh, "missingValue", CDI_default_missval), 0); const int param = gribapiGetParam(gh); int level1 = 0, level2 = 0, leveltype1, leveltype2 = -1, lbounds, level_sf, level_unit; var_tile_t tiles = dummy_tiles; gribGetLevel(gh, &leveltype1, &leveltype2, &lbounds, &level1, &level2, &level_sf, &level_unit, &tiles); gribapiGetString(gh, "shortName", varname, sizeof(varname)); int64_t vdate, sdate; int vtime, stime; gribapiGetValidityDateTime(gh, &vdate, &vtime, &sdate, &stime); DateTime datetime = { .date = vdate, .time = vtime }; if ( rindex == nrecs ) break; if ( rindex == 0 ) { datetime0 = datetime; const int taxisID = vlistInqTaxis(vlistID); if ( taxisInqType(taxisID) == TAXIS_RELATIVE ) { taxis->type = TAXIS_RELATIVE; taxis->unit = gribapiGetTimeUnits(gh); gribapiGetDataDateTime(gh, &(taxis->rdate), &(taxis->rtime)); } else { taxis->type = TAXIS_ABSOLUTE; } taxis->fdate = taxis->rdate; taxis->ftime = taxis->rtime; taxis->vdate = vdate; taxis->vtime = vtime; taxis->sdate = sdate; taxis->stime = stime; // printf("n: %d %6d %d %6d %d %6d\n", taxis->rdate, taxis->rtime, taxis->sdate, taxis->stime, taxis->vdate, taxis->vtime); } VarScanKeys scanKeys = gribapiGetScanKeys(gh); const int tsteptype = gribapiGetTsteptype(gh); const size_t gridsize = gribapiGetGridsize(gh); compvar2_t compVar = gribapiVarSet(param, level1, level2, leveltype1, tsteptype, gridsize, varname, scanKeys, tiles); for ( vrecID = 0; vrecID < nrecs; vrecID++ ) { recID = streamptr->tsteps[1].recIDs[vrecID]; if ( gribapiVarCompare(compVar, records[recID], 0) == 0 ) break; } if ( vrecID == nrecs ) { if ( CDI_inventory_mode == 1 ) { gribWarning("Parameter not defined at timestep 1!", nrecs_scanned, tsID+1, varname, param, level1, level2); return CDI_EUFSTRUCT; } else { gribWarning("Parameter not defined at timestep 1, skipped!", nrecs_scanned, tsID+1, varname, param, level1, level2); continue; } } if ( CDI_inventory_mode != 1 ) { if ( records[recID].used ) { if ( datetimeDiffer(datetime, datetime0) ) break; if ( CDI_Debug ) gribWarning("Parameter already exist, skipped!", nrecs_scanned, tsID+1, varname, param, level1, level2); continue; } } records[recID].used = true; streamptr->tsteps[tsID].recIDs[rindex] = recID; if ( CDI_Debug ) Message("%4d %8d %4d %8d %8lld %6d", rindex+1, (int)recpos, param, level1, vdate, vtime); if ( gribapiVarCompare(compVar, records[recID], 0) != 0 ) { Message("tsID = %d recID = %d param = %3d new %3d level = %3d new %3d", tsID, recID, records[recID].param, param, records[recID].ilevel, level1); Error("Invalid, unsupported or inconsistent record structure"); } records[recID].position = recpos; records[recID].size = recsize; if ( CDI_Debug ) Message("%4d %8d %4d %8d %8lld %6d", rindex, (int)recpos, param, level1, vdate, vtime); grib_handle_delete(gh); gh = NULL; rindex++; } if ( gh ) grib_handle_delete(gh); for ( vrecID = 0; vrecID < nrecs; vrecID++ ) { recID = streamptr->tsteps[tsID].recIDs[vrecID]; if ( ! records[recID].used ) break; } if ( vrecID < nrecs ) { gribWarning("Paramameter not found!", nrecs_scanned, tsID+1, varname, records[recID].param, records[recID].ilevel, records[recID].ilevel2); return CDI_EUFSTRUCT; } streamptr->rtsteps++; if ( streamptr->ntsteps != streamptr->rtsteps ) { tsID = tstepsNewEntry(streamptr); if ( tsID != streamptr->rtsteps ) Error("Internal error. tsID = %d", tsID); streamptr->tsteps[tsID-1].next = true; streamptr->tsteps[tsID].position = recpos; } fileSetPos(fileID, streamptr->tsteps[tsID].position, SEEK_SET); streamptr->tsteps[tsID].position = recpos; streamptr->record->buffer = gribbuffer; streamptr->record->buffersize = buffersize; } if ( nrecs > 0 && nrecs < streamptr->tsteps[tsID].nrecs ) { Warning("Incomplete timestep. Stop scanning at timestep %d.", tsID); streamptr->ntsteps = tsID; } return streamptr->ntsteps; } #ifdef gribWarning #undef gribWarning #endif int gribapiDecode(void *gribbuffer, size_t gribsize, double *data, size_t gridsize, int unreduced, size_t *nmiss, double missval) { int status = 0; if ( unreduced ) { static bool lwarn = true; if ( lwarn ) { lwarn = false; Warning("Conversion of gaussian reduced grids unsupported!"); } } size_t recsize = (size_t)gribsize; grib_handle *gh = grib_handle_new_from_message(NULL, gribbuffer, recsize); GRIB_CHECK(my_grib_set_double(gh, "missingValue", missval), 0); // get the size of the values array size_t datasize; GRIB_CHECK(grib_get_size(gh, "values", &datasize), 0); // long numberOfPoints; // GRIB_CHECK(grib_get_long(gh, "numberOfPoints", &numberOfPoints), 0); // printf("values_size = %d numberOfPoints = %ld\n", datasize, numberOfPoints); if ( gridsize != datasize ) Error("Internal problem: gridsize(%zu) != datasize(%zu)!", gridsize, datasize); size_t dummy = datasize; GRIB_CHECK(grib_get_double_array(gh, "values", data, &dummy), 0); long lpar; GRIB_CHECK(grib_get_long(gh, "gridDefinitionTemplateNumber", &lpar), 0); int gridtype = (int) lpar; *nmiss = 0; if ( gridtype < 50 || gridtype > 53 ) { GRIB_CHECK(grib_get_long(gh, "numberOfMissing", &lpar), 0); *nmiss = (int) lpar; // printf("gridtype %d, nmiss %d\n", gridtype, nmiss); } grib_handle_delete(gh); return status; } static void gribapiDefInstitut(grib_handle *gh, int vlistID, int varID) { int instID = (vlistInqInstitut(vlistID) != CDI_UNDEFID) ? vlistInqInstitut(vlistID) : vlistInqVarInstitut(vlistID, varID); if ( instID != CDI_UNDEFID ) { long center = institutInqCenter(instID); long subcenter = institutInqSubcenter(instID); long center0, subcenter0; GRIB_CHECK(grib_get_long(gh, "centre", ¢er0), 0); GRIB_CHECK(grib_get_long(gh, "subCentre", &subcenter0), 0); if ( center != center0 ) GRIB_CHECK(my_grib_set_long(gh, "centre", center), 0); if ( subcenter != subcenter0 ) GRIB_CHECK(my_grib_set_long(gh, "subCentre", subcenter), 0); } int status; int centre, subCentre; status = cdiInqKeyInt(vlistID, CDI_GLOBAL, CDI_KEY_CENTRE, ¢re); if ( status == 0 ) grib_set_long(gh, "centre", centre); status = cdiInqKeyInt(vlistID, CDI_GLOBAL, CDI_KEY_SUBCENTRE, &subCentre); if ( status == 0 ) grib_set_long(gh, "subCentre", subCentre); status = cdiInqKeyInt(vlistID, varID, CDI_KEY_CENTRE, ¢re); if ( status == 0 ) grib_set_long(gh, "centre", centre); status = cdiInqKeyInt(vlistID, varID, CDI_KEY_SUBCENTRE, &subCentre); if ( status == 0 ) grib_set_long(gh, "subCentre", subCentre); } static void gribapiDefModel(grib_handle *gh, int vlistID, int varID) { int modelID = (vlistInqModel(vlistID) != CDI_UNDEFID) ? vlistInqModel(vlistID) : vlistInqVarModel(vlistID, varID); if ( modelID != CDI_UNDEFID ) GRIB_CHECK(my_grib_set_long(gh, "generatingProcessIdentifier", modelInqGribID(modelID)), 0); } static void gribapiDefParam(int editionNumber, grib_handle *gh, int param, const char *name, const char *stdname) { bool ldefined = false; int pdis, pcat, pnum; cdiDecodeParam(param, &pnum, &pcat, &pdis); if ( pnum < 0 ) { size_t len = strlen(stdname); if ( len ) { int status = my_grib_set_string(gh, "cfName", stdname, &len); if ( status == 0 ) ldefined = true; else Warning("grib_api: No match for cfName=%s", stdname); } if ( ldefined == false ) { len = strlen(name); int status = my_grib_set_string(gh, "shortName", name, &len); if ( status == 0 ) ldefined = true; else Warning("grib_api: No match for shortName=%s", name); } } if ( ldefined == false ) { if ( pnum < 0 ) pnum = -pnum; if ( pnum > 255 ) { static bool lwarn_pnum = true; if ( lwarn_pnum ) { Warning("Parameter number %d out of range (1-255), set to %d!", pnum, pnum%256); lwarn_pnum = false; } pnum = pnum%256; } if ( editionNumber <= 1 ) { static bool lwarn_pdis = true; if ( pdis != 255 && lwarn_pdis ) { char paramstr[32]; cdiParamToString(param, paramstr, sizeof(paramstr)); Warning("Can't convert GRIB2 parameter ID (%s) to GRIB1, set to %d.%d!", paramstr, pnum, pcat); lwarn_pdis = false; } GRIB_CHECK(my_grib_set_long(gh, "table2Version", pcat), 0); GRIB_CHECK(my_grib_set_long(gh, "indicatorOfParameter", pnum), 0); } else { GRIB_CHECK(my_grib_set_long(gh, "discipline", pdis), 0); GRIB_CHECK(my_grib_set_long(gh, "parameterCategory", pcat), 0); GRIB_CHECK(my_grib_set_long(gh, "parameterNumber", pnum), 0); } } // printf("param: %d.%d.%d %s\n", pnum, pcat, pdis, name); } static int getTimeunitFactor(const int timeunit) { switch (timeunit) { case TUNIT_SECOND: return 1; case TUNIT_MINUTE: return 60; case TUNIT_HOUR: return 3600; case TUNIT_3HOURS: return 10800; case TUNIT_6HOURS: return 21600; case TUNIT_12HOURS: return 43200; case TUNIT_DAY: return 86400; } return 3600; } static int grib2ProDefTempHasStatisticalDef(const int proDefTempNum) { switch (proDefTempNum) { case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 34: case 42: case 43: case 46: case 47: case 61: case 91: case 1001: case 1101: case 40034: return 1; } return 0; } static int getUnitsOfTime(const int timeunit) { switch (timeunit) { case TUNIT_SECOND: return 13; case TUNIT_MINUTE: return 0; case TUNIT_HOUR: return 1; case TUNIT_3HOURS: return 10; case TUNIT_6HOURS: return 11; case TUNIT_12HOURS: return 12; case TUNIT_DAY: return 2; } return 1; } static void gribapiDefStepUnits(int editionNumber, grib_handle *gh, int timeunit, int proDefTempNum, int gcinit) { if ( !gcinit ) { long unitsOfTime = getUnitsOfTime(timeunit); GRIB_CHECK(my_grib_set_long(gh, "stepUnits", unitsOfTime), 0); if ( editionNumber == 1 ) { GRIB_CHECK(my_grib_set_long(gh, "unitOfTimeRange", unitsOfTime), 0); } else if ( grib2ProDefTempHasStatisticalDef(proDefTempNum) ) { GRIB_CHECK(my_grib_set_long(gh, "indicatorOfUnitForTimeRange", unitsOfTime), 0); GRIB_CHECK(my_grib_set_long(gh, "indicatorOfUnitOfTimeRange", unitsOfTime), 0); } else { // NOTE KNMI: HIRLAM model files LAMH_D11 are in grib1 and do NOT have key indicatorOfUnitForTimeRange // Watch out for compatibility issues. GRIB_CHECK(my_grib_set_long(gh, "indicatorOfUnitOfTimeRange", unitsOfTime), 0); } } } static int gribapiDefSteptype(int editionNumber, grib_handle *gh, int productDefinitionTemplate, int typeOfGeneratingProcess, int tsteptype, int gcinit) { const char *stepType = "instant"; long proDefTempNum = 0; if ( tsteptype >= TSTEP_INSTANT && tsteptype <= TSTEP_SUM ) { stepType = cdiGribAPI_ts_str_map[tsteptype].sname; proDefTempNum = cdiGribAPI_ts_str_map[tsteptype].productionTemplate; } if ( typeOfGeneratingProcess == 4 ) { proDefTempNum = (proDefTempNum == 8) ? 11 : 1; } if ( productDefinitionTemplate != -1 ) proDefTempNum = productDefinitionTemplate; if ( !gcinit ) { if ( editionNumber > 1 ) GRIB_CHECK(my_grib_set_long(gh, "productDefinitionTemplateNumber", proDefTempNum), 0); size_t len = strlen(stepType); int status = my_grib_set_string(gh, "stepType", stepType, &len); if (status != 0) GRIB_CHECK(my_grib_set_long(gh, "productDefinitionTemplateNumber", 0), 0); } return (int)proDefTempNum; } static void gribapiDefDateTimeAbs(int editionNumber, grib_handle *gh, int64_t date, int time, int productDefinitionTemplate, int typeOfGeneratingProcess, int tsteptype, int gcinit) { (void ) gribapiDefSteptype(editionNumber, gh, productDefinitionTemplate, typeOfGeneratingProcess, tsteptype, gcinit); if ( editionNumber > 1 ) GRIB_CHECK(my_grib_set_long(gh, "significanceOfReferenceTime", 0), 0); if ( editionNumber > 1 ) GRIB_CHECK(my_grib_set_long(gh, "stepRange", 0), 0); if ( date == 0 ) date = 10101; gribapiSetDataDateTime(gh, date, time); } static int gribapiDefDateTimeRel(int editionNumber, grib_handle *gh, int64_t fdate, int ftime, int64_t vdate, int vtime, int64_t sdate, int stime, int productDefinitionTemplate, int typeOfGeneratingProcess, int tsteptype, int timeunit, int calendar, int gcinit) { int status = -1; int year, month, day, hour, minute, second; int64_t julday1, julday2, days; int secofday1, secofday2, secs; cdiDecodeDate(fdate, &year, &month, &day); cdiDecodeTime(ftime, &hour, &minute, &second); encode_juldaysec(calendar, year, month, day, hour, minute, second, &julday1, &secofday1); if ( vdate == 0 && vtime == 0 ) { vdate = fdate; vtime = ftime; } cdiDecodeDate(vdate, &year, &month, &day); cdiDecodeTime(vtime, &hour, &minute, &second); encode_juldaysec(calendar, year, month, day, hour, minute, second, &julday2, &secofday2); (void) julday_sub(julday1, secofday1, julday2, secofday2, &days, &secs); const int factor = getTimeunitFactor(timeunit); if ( !(int)(fmod(days*86400.0 + secs, factor))) { const int proDefTempNum = gribapiDefSteptype(editionNumber, gh, productDefinitionTemplate, typeOfGeneratingProcess, tsteptype, gcinit); gribapiDefStepUnits(editionNumber, gh, timeunit, proDefTempNum, gcinit); long startStep = 0; const double endStepF = (days*86400.0 + secs)/factor; const long maxStep = (editionNumber > 1) ? INT_MAX : 65000; if (endStepF > maxStep) return status; long endStep = (long) endStepF; if (sdate != 0 && (tsteptype == TSTEP_RANGE || tsteptype == TSTEP_AVG || tsteptype == TSTEP_ACCUM || tsteptype == TSTEP_DIFF)) { cdiDecodeDate(sdate, &year, &month, &day); cdiDecodeTime(stime, &hour, &minute, &second); encode_juldaysec(calendar, year, month, day, hour, minute, second, &julday2, &secofday2); (void) julday_sub(julday1, secofday1, julday2, secofday2, &days, &secs); startStep = (long) ((days*86400.0 + secs)/factor); } if ( editionNumber > 1 ) GRIB_CHECK(my_grib_set_long(gh, "significanceOfReferenceTime", 1), 0); if ( editionNumber > 1 ) GRIB_CHECK(my_grib_set_long(gh, "stepRange", 0), 0); if ( fdate == 0 ) fdate = 10101; gribapiSetDataDateTime(gh, fdate, ftime); // printf(">>>>> tsteptype %d startStep %ld endStep %ld\n", tsteptype, startStep, endStep); // Product Definition Template Number: defined in GRIB_API file 4.0.table point in time products: if ( (proDefTempNum >= 0 && proDefTempNum <= 7) || proDefTempNum == 55 || proDefTempNum == 40055 ) // Tile startStep = endStep; if (endStep < startStep || startStep > 255 || endStep > 255) { startStep = 0; endStep = 0; } else { status = 0; } if ( editionNumber > 1 ) GRIB_CHECK(my_grib_set_long(gh, "forecastTime", startStep), 0); //if ( editionNumber == 1 && startStep > 0) GRIB_CHECK(my_grib_set_long(gh, "startStep", startStep), 0); if ( editionNumber == 1 ) GRIB_CHECK(my_grib_set_long(gh, "startStep", startStep), 0); GRIB_CHECK(my_grib_set_long(gh, "endStep", endStep), 0); } return status; } static void gribapiDefTime(int editionNumber, int productDefinitionTemplate, int typeOfGeneratingProcess, grib_handle *gh, int64_t vdate, int vtime, int tsteptype, int numavg, int taxisID, int gcinit) { UNUSED(numavg); int taxistype = -1; if ( taxisID != -1 ) taxistype = taxisInqType(taxisID); if ( typeOfGeneratingProcess == 196 ) { vdate = 10101; vtime = 0; taxistype = TAXIS_ABSOLUTE; } /* else if ( typeOfGeneratingProcess == 9 ) { } */ if ( taxistype == TAXIS_RELATIVE ) { const int timeunit = taxisInqTunit(taxisID); const int calendar = taxisInqCalendar(taxisID); int64_t fdate = taxisInqFdate(taxisID); int ftime = taxisInqFtime(taxisID); if (fdate == CDI_UNDEFID) { fdate = taxisInqRdate(taxisID); ftime = taxisInqRtime(taxisID); } if (vdate < fdate || (vdate == fdate && vtime < ftime)) { fdate = vdate; ftime = vtime; } int64_t sdate = taxisInqSdate(taxisID); int stime = taxisInqStime(taxisID); int status = gribapiDefDateTimeRel(editionNumber, gh, fdate, ftime, vdate, vtime, sdate, stime, productDefinitionTemplate, typeOfGeneratingProcess, tsteptype, timeunit, calendar, gcinit); if ( status != 0 ) taxistype = TAXIS_ABSOLUTE; } if ( taxistype == TAXIS_ABSOLUTE ) { gribapiDefDateTimeAbs(editionNumber, gh, vdate, vtime, productDefinitionTemplate, typeOfGeneratingProcess, tsteptype, gcinit); } } static void gribapiDefGridRegular(grib_handle *gh, int gridID, int gridtype, bool gridIsRotated, bool gridIsCurvilinear, int uvRelativeToGrid) { const char *mesg; size_t len; if ( gridtype == GRID_GAUSSIAN ) { static const char mesg_gaussian[] = "regular_gg"; len = sizeof(mesg_gaussian) - 1; mesg = mesg_gaussian; } else if ( gridtype == GRID_GAUSSIAN_REDUCED ) { static const char mesg_gaussian_reduced[] = "reduced_gg"; len = sizeof(mesg_gaussian_reduced) - 1; mesg = mesg_gaussian_reduced; } else if ( gridIsRotated ) { static const char mesg_rot_lonlat[] = "rotated_ll"; len = sizeof(mesg_rot_lonlat) - 1; mesg = mesg_rot_lonlat; } else { static const char mesg_regular_ll[] = "regular_ll"; len = sizeof(mesg_regular_ll) - 1; mesg = mesg_regular_ll; } GRIB_CHECK(my_grib_set_string(gh, "gridType", mesg, &len), 0); double xfirst = 0, xlast = 0, xinc = 0; double yfirst = 0, ylast = 0, yinc = 0; size_t nlon = gridInqXsize(gridID); size_t nlat = gridInqYsize(gridID); if ( gridtype == GRID_GAUSSIAN_REDUCED ) { nlon = 0; int *reducedPoints = (int *) Malloc(nlat*sizeof(int)); long *pl = (long *) Malloc(nlat*sizeof(long)); gridInqReducedPoints(gridID, reducedPoints); for ( size_t i = 0; i < nlat; ++i ) pl[i] = reducedPoints[i]; GRIB_CHECK(grib_set_long_array(gh, "pl", pl, nlat), 0); Free(pl); Free(reducedPoints); xfirst = 0; xinc = 360. * 0.5 / (double)nlat; xlast = 360. - 360. * 0.5 / (double)nlat; } else { if ( nlon == 0 ) nlon = 1; else { xfirst = gridInqXval(gridID, 0); xlast = gridInqXval(gridID, (gridIsCurvilinear ? nlon*nlat : nlon) - 1); xinc = fabs(gridInqXinc(gridID)); } } if ( nlat == 0 ) nlat = 1; else { yfirst = gridInqYval(gridID, 0); ylast = gridInqYval(gridID, (gridIsCurvilinear ? nlon*nlat : nlat) - 1); yinc = fabs(gridInqYinc(gridID)); } double xfirsto = xfirst; double xlasto = xlast; while ( xfirsto > 360 ) xfirsto -= 360; while ( xlasto > 360 ) xlasto -= 360; if ( gridtype != GRID_GAUSSIAN_REDUCED ) GRIB_CHECK(my_grib_set_long(gh, "Ni", nlon), 0); GRIB_CHECK(my_grib_set_double(gh, "longitudeOfFirstGridPointInDegrees", xfirsto), 0); GRIB_CHECK(my_grib_set_double(gh, "longitudeOfLastGridPointInDegrees", xlasto), 0); if ( gridtype != GRID_GAUSSIAN_REDUCED ) GRIB_CHECK(my_grib_set_double(gh, "iDirectionIncrementInDegrees", xinc), 0); GRIB_CHECK(my_grib_set_long(gh, "Nj", (long)nlat), 0); GRIB_CHECK(my_grib_set_double(gh, "latitudeOfFirstGridPointInDegrees", yfirst), 0); GRIB_CHECK(my_grib_set_double(gh, "latitudeOfLastGridPointInDegrees", ylast), 0); long xscan = xfirst > xlast; GRIB_CHECK(my_grib_set_long(gh, "iScansNegatively", xscan), 0); xscan = yfirst < ylast; GRIB_CHECK(my_grib_set_long(gh, "jScansPositively", xscan), 0); if ( gridtype == GRID_GAUSSIAN || gridtype == GRID_GAUSSIAN_REDUCED ) { int np = gridInqNP(gridID); if ( np == 0 ) np = nlat/2; GRIB_CHECK(my_grib_set_long(gh, "numberOfParallelsBetweenAPoleAndTheEquator", np), 0); } else { GRIB_CHECK(my_grib_set_double(gh, "jDirectionIncrementInDegrees", yinc), 0); } if ( gridIsRotated ) { double xpole = 0, ypole = 0, angle = 0; gridInqParamRLL(gridID, &xpole, &ypole, &angle); xpole += 180; if ( fabs(ypole) > 0 ) ypole = -ypole; // change from north to south pole if ( fabs(angle) > 0 ) angle = -angle; GRIB_CHECK(my_grib_set_double(gh, "latitudeOfSouthernPoleInDegrees", ypole), 0); GRIB_CHECK(my_grib_set_double(gh, "longitudeOfSouthernPoleInDegrees", xpole), 0); GRIB_CHECK(my_grib_set_double(gh, "angleOfRotation", angle), 0); } if ( uvRelativeToGrid >= 0 ) GRIB_CHECK(my_grib_set_long(gh, "uvRelativeToGrid", uvRelativeToGrid), 0); } static long radiusToShapeOfTheEarth(double radius) { long shapeOfTheEarth = 0; if (IS_EQUAL(radius, 6367470.)) shapeOfTheEarth = 0; else if (IS_EQUAL(radius, 6378160.)) shapeOfTheEarth = 2; else if (IS_EQUAL(radius, 6371229.)) shapeOfTheEarth = 6; else if (IS_EQUAL(radius, 6371200.)) shapeOfTheEarth = 8; return shapeOfTheEarth; } static void gribapiDefGridLCC(grib_handle *gh, int editionNumber, int gridID, int uvRelativeToGrid) { const long xsize = (long) gridInqXsize(gridID); const long ysize = (long) gridInqYsize(gridID); double lon_0, lat_0, lat_1, lat_2, a, rf, xval_0, yval_0, x_0, y_0; gridInqParamLCC(gridID, CDI_grid_missval, &lon_0, &lat_0, &lat_1, &lat_2, &a, &rf, &xval_0, &yval_0, &x_0, &y_0); gridVerifyGribParamLCC(CDI_grid_missval, &lon_0, &lat_0, &lat_1, &lat_2, &a, &rf, &xval_0, &yval_0, &x_0, &y_0); if ( xval_0 < 0 ) xval_0 += 360; if ( lon_0 < 0 ) lon_0 += 360; const bool lsouth = (lat_1 < 0); if ( lsouth ) { lat_1 = -lat_2; lat_2 = -lat_2; } int projflag = 0; if ( lsouth ) gribbyte_set_bit(&projflag, 1); double xinc = gridInqXinc(gridID); double yinc = gridInqYinc(gridID); static const char mesg[] = "lambert"; size_t len = sizeof(mesg) -1; GRIB_CHECK(my_grib_set_string(gh, "gridType", mesg, &len), 0); GRIB_CHECK(my_grib_set_long(gh, "Nx", xsize), 0); GRIB_CHECK(my_grib_set_long(gh, "Ny", ysize), 0); GRIB_CHECK(my_grib_set_long(gh, "DxInMetres", lround(xinc)), 0); GRIB_CHECK(my_grib_set_long(gh, "DyInMetres", lround(yinc)), 0); GRIB_CHECK(my_grib_set_double(gh, "longitudeOfFirstGridPointInDegrees", xval_0), 0); GRIB_CHECK(my_grib_set_double(gh, "latitudeOfFirstGridPointInDegrees", yval_0), 0); GRIB_CHECK(my_grib_set_double(gh, "LoVInDegrees", lon_0), 0); GRIB_CHECK(my_grib_set_double(gh, "Latin1InDegrees", lat_1), 0); GRIB_CHECK(my_grib_set_double(gh, "Latin2InDegrees", lat_2), 0); GRIB_CHECK(my_grib_set_long(gh, "projectionCentreFlag", projflag), 0); const long shapeOfTheEarth = radiusToShapeOfTheEarth(a); if ( shapeOfTheEarth ) GRIB_CHECK(my_grib_set_long(gh, "shapeOfTheEarth", shapeOfTheEarth), 0); if ( uvRelativeToGrid >= 0 ) GRIB_CHECK(my_grib_set_long(gh, "uvRelativeToGrid", uvRelativeToGrid), 0); const long earthIsOblate = (IS_EQUAL(a, 6378160.) && IS_EQUAL(rf, 297.)); if ( earthIsOblate ) GRIB_CHECK(my_grib_set_long(gh, "earthIsOblate", earthIsOblate), 0); int scanflag = 0; gribbyte_set_bit(&scanflag, 2); if ( editionNumber <= 1 ) GRIB_CHECK(my_grib_set_long(gh, "scanningMode", (long)scanflag), 0); } static void gribapiDefGridSTERE(grib_handle *gh, int gridID) { const long xsize = (long) gridInqXsize(gridID); const long ysize = (long) gridInqYsize(gridID); double lon_0, lat_ts, lat_0, a, xval_0, yval_0, x_0, y_0; gridInqParamSTERE(gridID, CDI_grid_missval, &lon_0, &lat_ts, &lat_0, &a, &xval_0, &yval_0, &x_0, &y_0); gridVerifyGribParamSTERE(CDI_grid_missval, &lon_0, &lat_ts, &lat_0, &a, &xval_0, &yval_0, &x_0, &y_0); if ( xval_0 < 0 ) xval_0 += 360; int projflag = 0; const double xinc = gridInqXinc(gridID); const double yinc = gridInqYinc(gridID); static const char mesg[] = "polar_stereographic"; size_t len = sizeof(mesg) -1; GRIB_CHECK(my_grib_set_string(gh, "gridType", mesg, &len), 0); GRIB_CHECK(my_grib_set_long(gh, "Nx", xsize), 0); GRIB_CHECK(my_grib_set_long(gh, "Ny", ysize), 0); GRIB_CHECK(my_grib_set_long(gh, "DxInMetres", lround(xinc)), 0); GRIB_CHECK(my_grib_set_long(gh, "DyInMetres", lround(yinc)), 0); GRIB_CHECK(my_grib_set_double(gh, "longitudeOfFirstGridPointInDegrees", xval_0), 0); GRIB_CHECK(my_grib_set_double(gh, "latitudeOfFirstGridPointInDegrees", yval_0), 0); GRIB_CHECK(my_grib_set_double(gh, "LaDInDegrees", lat_ts), 0); GRIB_CHECK(my_grib_set_double(gh, "orientationOfTheGridInDegrees", lon_0), 0); const long southPoleOnProjectionPlane = IS_EQUAL(lat_0, -90.); GRIB_CHECK(my_grib_set_double(gh, "southPoleOnProjectionPlane", southPoleOnProjectionPlane), 0); GRIB_CHECK(my_grib_set_long(gh, "projectionCentreFlag", projflag), 0); const long shapeOfTheEarth = radiusToShapeOfTheEarth(a); if ( shapeOfTheEarth ) GRIB_CHECK(my_grib_set_long(gh, "shapeOfTheEarth", shapeOfTheEarth), 0); GRIB_CHECK(my_grib_set_long(gh, "resolutionAndComponentFlags", 8), 0); GRIB_CHECK(my_grib_set_long(gh, "iScansNegatively", 0), 0); GRIB_CHECK(my_grib_set_long(gh, "jScansPositively", 1), 0); } static void gribapiDefGridGME(grib_handle *gh, int gridID, long gridsize) { GRIB_CHECK(my_grib_set_long(gh, "gridDefinitionTemplateNumber", GRIB2_GTYPE_GME), 0); int nd = 0, ni = 0, ni2 = 0, ni3 = 0; gridInqParamGME(gridID, &nd, &ni, &ni2, &ni3); GRIB_CHECK(my_grib_set_long(gh, "nd", nd), 0); GRIB_CHECK(my_grib_set_long(gh, "Ni", ni), 0); GRIB_CHECK(my_grib_set_long(gh, "n2", ni2), 0); GRIB_CHECK(my_grib_set_long(gh, "n3", ni3), 0); GRIB_CHECK(my_grib_set_long(gh, "latitudeOfThePolePoint", 90000000), 0); GRIB_CHECK(my_grib_set_long(gh, "longitudeOfThePolePoint", 0), 0); GRIB_CHECK(my_grib_set_long(gh, "numberOfDataPoints", gridsize), 0); GRIB_CHECK(my_grib_set_long(gh, "totalNumberOfGridPoints", gridsize), 0); } static void gribapiDefGridUnstructured(grib_handle *gh, int gridID) { static bool warning = true; const int status = my_grib_set_long(gh, "gridDefinitionTemplateNumber", GRIB2_GTYPE_UNSTRUCTURED); if ( status != 0 && warning ) { warning = false; Warning("Can't write reference grid!"); Warning("gridDefinitionTemplateNumber %d not found (grib2/template.3.%d.def)!", GRIB2_GTYPE_UNSTRUCTURED, GRIB2_GTYPE_UNSTRUCTURED); } else { int number = 0; cdiInqKeyInt(gridID, CDI_GLOBAL, CDI_KEY_NUMBEROFGRIDUSED, &number); if ( number < 0 ) number = 0; GRIB_CHECK(my_grib_set_long(gh, "numberOfGridUsed", number), 0); int position = 0; cdiInqKeyInt(gridID, CDI_GLOBAL, CDI_KEY_NUMBEROFGRIDINREFERENCE, &position); if ( position < 0 ) position = 0; GRIB_CHECK(my_grib_set_long(gh, "numberOfGridInReference", position), 0); unsigned char uuid[CDI_UUID_SIZE]; size_t len = CDI_UUID_SIZE; memset(uuid, 0, len); int length = CDI_UUID_SIZE; cdiInqKeyBytes(gridID, CDI_GLOBAL, CDI_KEY_UUID, uuid, &length); if (grib_set_bytes(gh, "uuidOfHGrid", uuid, &len) != 0) Warning("Can't write UUID!"); } } static void gribapiDefGridSpectral(grib_handle *gh, int gridID) { const int trunc = gridInqTrunc(gridID); enum { numTruncAtt = 3 }; static const char truncAttNames[numTruncAtt][2] = { "J", "K", "M" }; for (size_t i = 0; i < numTruncAtt; ++i) GRIB_CHECK(my_grib_set_long(gh, truncAttNames[i], trunc), 0); if ( gridInqComplexPacking(gridID) ) { static const char truncAttNames2[numTruncAtt][3] = { "JS", "KS", "MS" }; for (size_t i = 0; i < numTruncAtt; ++i) GRIB_CHECK(my_grib_set_long(gh, truncAttNames2[i], 20), 0); } } static void gribapiDefPackingType(grib_handle *gh, bool lieee, bool lspectral, bool lcomplex, int comptype, size_t gridsize) { const char *mesg; size_t len; if ( lspectral ) { if ( lcomplex ) { static const char mesg_spectral_complex[] = "spectral_complex"; len = sizeof (mesg_spectral_complex) - 1; mesg = mesg_spectral_complex; } else { static const char mesg_spectral_simple[] = "spectral_simple"; len = sizeof (mesg_spectral_simple) - 1; mesg = mesg_spectral_simple; } } else if ( comptype == CDI_COMPRESS_JPEG && gridsize > 1 ) { static const char mesg_grid_jpeg[] = "grid_jpeg"; len = sizeof (mesg_grid_jpeg) - 1; mesg = mesg_grid_jpeg; } else if ( (comptype == CDI_COMPRESS_SZIP || comptype == CDI_COMPRESS_AEC) && gridsize > 1 ) { static const char mesg_grid_ccsds[] = "grid_ccsds"; len = sizeof (mesg_grid_ccsds) - 1; mesg = mesg_grid_ccsds; } else if ( lieee ) { static const char mesg_ieee[] = "grid_ieee"; len = sizeof (mesg_ieee) - 1; mesg = mesg_ieee; } else { static const char mesg_simple[] = "grid_simple"; len = sizeof (mesg_simple) - 1; mesg = mesg_simple; } GRIB_CHECK(my_grib_set_string(gh, "packingType", mesg, &len), 0); } static void gribapiDefGrid(int editionNumber, grib_handle *gh, int gridID, int comptype, int datatype, int uvRelativeToGrid) { const size_t gridsize = gridInqSize(gridID); bool gridIsRotated = false; bool gridIsCurvilinear = false; const int gridtype = grbGetGridtype(&gridID, gridsize, &gridIsRotated, &gridIsCurvilinear); bool lieee = (editionNumber == 2 && (datatype == CDI_DATATYPE_FLT32 || datatype == CDI_DATATYPE_FLT64)); bool lspectral = (gridtype == GRID_SPECTRAL); bool lcomplex = (lspectral && gridInqComplexPacking(gridID)); if ( lieee ) comptype = 0; if ( lspectral ) lieee = false; if ( lspectral ) // gridType needs to be defined before packingType !!! { static const char mesg[] = "sh"; size_t len = sizeof (mesg) -1; GRIB_CHECK(my_grib_set_string(gh, "gridType", mesg, &len), 0); } gribapiDefPackingType(gh, lieee, lspectral, lcomplex, comptype, gridsize); if ( lieee ) GRIB_CHECK(my_grib_set_long(gh, "precision", datatype == CDI_DATATYPE_FLT64 ? 2 : 1), 0); if ( editionNumber == 2 ) GRIB_CHECK(my_grib_set_long(gh, "numberOfValues", (long)gridsize), 0); switch (gridtype) { case GRID_LONLAT: case GRID_GAUSSIAN: case GRID_GAUSSIAN_REDUCED: case GRID_TRAJECTORY: { gribapiDefGridRegular(gh, gridID, gridtype, gridIsRotated, gridIsCurvilinear, uvRelativeToGrid); break; } case CDI_PROJ_LCC: { gribapiDefGridLCC(gh, editionNumber, gridID, uvRelativeToGrid); break; } case CDI_PROJ_STERE: { gribapiDefGridSTERE(gh, gridID); break; } case GRID_SPECTRAL: { gribapiDefGridSpectral(gh, gridID); break; } case GRID_GME: { if ( editionNumber <= 1 ) Error("GME grid can't be stored in GRIB edition %d!", editionNumber); gribapiDefGridGME(gh, gridID, (long) gridsize); break; } case GRID_UNSTRUCTURED: { if ( editionNumber <= 1 ) Error("Unstructured grid can't be stored in GRIB edition %d!", editionNumber); gribapiDefGridUnstructured(gh, gridID); break; } default: { Error("Unsupported grid type: %s", gridNamePtr(gridtype)); break; } } } static void getLevelFactor(double level, long *factor, long *out_scaled_value) { double scaled_value = level; long iscaled_value = lround(scaled_value); long i; const double eps = 1.e-8; for ( i=0; (fabs(scaled_value - (double) iscaled_value) >= eps) && i < 7; i++ ) { scaled_value *= 10.; iscaled_value = lround(scaled_value); } (*factor) = i; (*out_scaled_value) = iscaled_value; } static void gribapiDefLevelType(grib_handle *gh, int gcinit, const char *keyname, long leveltype) { bool lset = false; if ( (leveltype == GRIB1_LTYPE_ISOBARIC_PA || leveltype == 99 || leveltype == 100) && gribEditionNumber(gh) == 1 ) { if ( gribGetLong(gh, "indicatorOfTypeOfLevel") != leveltype ) lset = true; } if ( !gcinit || lset ) GRIB_CHECK(my_grib_set_long(gh, keyname, leveltype), 0); } static void grib1DefLevel(grib_handle *gh, int gcinit, long leveltype, bool lbounds, double level, double dlevel1, double dlevel2) { gribapiDefLevelType(gh, gcinit, "indicatorOfTypeOfLevel", leveltype); if ( lbounds ) { GRIB_CHECK(my_grib_set_long(gh, "topLevel", lround(dlevel1)), 0); GRIB_CHECK(my_grib_set_long(gh, "bottomLevel", lround(dlevel2)), 0); } else { GRIB_CHECK(my_grib_set_long(gh, "level", lround(level)), 0); } } static void grib2DefLevel(grib_handle *gh, int gcinit, long leveltype1, long leveltype2, bool lbounds, double level, double dlevel1, double dlevel2) { gribapiDefLevelType(gh, gcinit, "typeOfFirstFixedSurface", leveltype1); if ( lbounds ) gribapiDefLevelType(gh, gcinit, "typeOfSecondFixedSurface", leveltype2); if ( !lbounds ) dlevel1 = level; long scaled_level, factor; getLevelFactor(dlevel1, &factor, &scaled_level); GRIB_CHECK(my_grib_set_long(gh, "scaleFactorOfFirstFixedSurface", factor), 0); GRIB_CHECK(my_grib_set_long(gh, "scaledValueOfFirstFixedSurface", scaled_level), 0); if ( lbounds ) { getLevelFactor(dlevel2, &factor, &scaled_level); GRIB_CHECK(my_grib_set_long(gh, "scaleFactorOfSecondFixedSurface", factor), 0); GRIB_CHECK(my_grib_set_long(gh, "scaledValueOfSecondFixedSurface", scaled_level), 0); } } static void gribapiDefLevel(int editionNumber, grib_handle *gh, int zaxisID, int levelID, int gcinit, int proddef_template_num) { char units[CDI_MAX_NAME]; bool lbounds = false; int zaxistype = zaxisInqType(zaxisID); int ltype = 0, ltype2 = -1; cdiInqKeyInt(zaxisID, CDI_GLOBAL, CDI_KEY_TYPEOFFIRSTFIXEDSURFACE, <ype); cdiInqKeyInt(zaxisID, CDI_GLOBAL, CDI_KEY_TYPEOFSECONDFIXEDSURFACE, <ype2); double level = zaxisInqLevels(zaxisID, NULL) ? zaxisInqLevel(zaxisID, levelID) : levelID+1; double dlevel1 = level, dlevel2 = 0; if ( zaxisInqLbounds(zaxisID, NULL) && zaxisInqUbounds(zaxisID, NULL) ) { lbounds = true; dlevel1 = zaxisInqLbound(zaxisID, levelID); dlevel2 = zaxisInqUbound(zaxisID, levelID); } if ( zaxistype == ZAXIS_GENERIC && ltype == 0 ) { Message("Changed zaxis type from %s to %s", zaxisNamePtr(zaxistype), zaxisNamePtr(ZAXIS_PRESSURE)); zaxistype = ZAXIS_PRESSURE; zaxisChangeType(zaxisID, zaxistype); cdiDefKeyString(zaxisID, CDI_GLOBAL, CDI_KEY_UNITS, "Pa"); } long grib_ltype = (editionNumber <= 1) ? zaxisTypeToGrib1ltype(zaxistype) : zaxisTypeToGrib2ltype(zaxistype); long grib_ltype2 = (ltype != ltype2 && ltype2 != -1) ? ltype2 : grib_ltype; switch (zaxistype) { case ZAXIS_SURFACE: case ZAXIS_MEANSEA: case ZAXIS_HEIGHT: case ZAXIS_ALTITUDE: case ZAXIS_SIGMA: case ZAXIS_DEPTH_BELOW_SEA: case ZAXIS_ISENTROPIC: { if ( zaxistype == ZAXIS_HEIGHT ) { double sf = 1; int length = CDI_MAX_NAME; cdiInqKeyString(zaxisID, CDI_GLOBAL, CDI_KEY_UNITS, units, &length); if ( units[1] == 'm' && !units[2] ) { if ( units[0] == 'c' ) sf = 0.01; else if ( units[0] == 'd' ) sf = 0.1; else if ( units[0] == 'k' ) sf = 1000; } if ( IS_NOT_EQUAL(sf, 1) ) { level *= sf; dlevel1 *= sf; dlevel2 *= sf; } } if ( editionNumber <= 1 ) { grib1DefLevel(gh, gcinit, grib_ltype, lbounds, level, dlevel1, dlevel2); } else { /* PRODUCT DEFINITION TEMPLATE NUMBER 32: "Analysis or forecast at a horizontal level or in a horizontal layer at a point in time for simulate (synthetic) satellite data" The key/value pairs that are set in "grib2DefLevel" do not exist for this template. */ if ( proddef_template_num != 32 ) grib2DefLevel(gh, gcinit, grib_ltype, grib_ltype2, lbounds, level, dlevel1, dlevel2); } break; } case ZAXIS_CLOUD_BASE: case ZAXIS_CLOUD_TOP: case ZAXIS_ISOTHERM_ZERO: case ZAXIS_TOA: case ZAXIS_SEA_BOTTOM: case ZAXIS_LAKE_BOTTOM: case ZAXIS_SEDIMENT_BOTTOM: case ZAXIS_SEDIMENT_BOTTOM_TA: case ZAXIS_SEDIMENT_BOTTOM_TW: case ZAXIS_MIX_LAYER: case ZAXIS_ATMOSPHERE: { if ( editionNumber <= 1 ) grib1DefLevel(gh, gcinit, grib_ltype, lbounds, level, dlevel1, dlevel2); else grib2DefLevel(gh, gcinit, grib_ltype, grib_ltype2, lbounds, level, dlevel1, dlevel2); break; } case ZAXIS_HYBRID: case ZAXIS_HYBRID_HALF: { if ( editionNumber <= 1 ) { grib_ltype = lbounds ? GRIB1_LTYPE_HYBRID_LAYER : GRIB1_LTYPE_HYBRID; grib1DefLevel(gh, gcinit, grib_ltype, lbounds, level, dlevel1, dlevel2); } else { grib2DefLevel(gh, gcinit, grib_ltype, grib_ltype, lbounds, level, dlevel1, dlevel2); } if ( !gcinit ) { int vctsize = zaxisInqVctSize(zaxisID); if ( vctsize > 0 ) { GRIB_CHECK(my_grib_set_long(gh, "PVPresent", 1), 0); GRIB_CHECK(grib_set_double_array(gh, "pv", zaxisInqVctPtr(zaxisID), (size_t)vctsize), 0); } } break; } case ZAXIS_PRESSURE: { if ( level < 0 ) Warning("Pressure level of %f Pa is below zero!", level); int length = CDI_MAX_NAME; cdiInqKeyString(zaxisID, CDI_GLOBAL, CDI_KEY_UNITS, units, &length); if ( units[0] && (units[0] != 'P') | (units[1] != 'a') ) { level *= 100; dlevel1 *= 100; dlevel2 *= 100; } if ( editionNumber <= 1 ) { double dum; if ( level < 32768 && (level < 100 || modf(level/100, &dum) > 0) ) grib_ltype = GRIB1_LTYPE_ISOBARIC_PA; else level /= 100; grib1DefLevel(gh, gcinit, grib_ltype, lbounds, level, dlevel1, dlevel2); } else { if ( ltype2 == -1 ) ltype2 = GRIB2_LTYPE_ISOBARIC; grib2DefLevel(gh, gcinit, GRIB2_LTYPE_ISOBARIC, ltype2, lbounds, level, dlevel1, dlevel2); } break; } case ZAXIS_SNOW: { if ( editionNumber <= 1 ) ; // not available else { grib2DefLevel(gh, gcinit, grib_ltype, grib_ltype, lbounds, level, dlevel1, dlevel2); } break; } case ZAXIS_DEPTH_BELOW_LAND: { int length = CDI_MAX_NAME; cdiInqKeyString(zaxisID, CDI_GLOBAL, CDI_KEY_UNITS, units, &length); double sf; //scalefactor if ( editionNumber <= 1 ) { if ( memcmp(units, "mm", 2) == 0 ) sf = 0.1; else if ( memcmp(units, "cm", 2) == 0 ) sf = 1; // cm else if ( memcmp(units, "dm", 2) == 0 ) sf = 10; else sf = 100; grib1DefLevel(gh, gcinit, grib_ltype, lbounds, level*sf, dlevel1*sf, dlevel2*sf); } else { if ( memcmp(units, "mm", 2) == 0 ) sf = 0.001; else if ( memcmp(units, "cm", 2) == 0 ) sf = 0.01; else if ( memcmp(units, "dm", 2) == 0 ) sf = 0.1; else sf = 1; // meter grib2DefLevel(gh, gcinit, grib_ltype, grib_ltype, lbounds, level*sf, dlevel1*sf, dlevel2*sf); } break; } case ZAXIS_REFERENCE: { if ( !gcinit ) GRIB_CHECK(my_grib_set_long(gh, "genVertHeightCoords", 1), 0); if ( editionNumber <= 1 ) ; // not available else { if ( lbounds ) { gribapiDefLevelType(gh, gcinit, "typeOfFirstFixedSurface", grib_ltype); gribapiDefLevelType(gh, gcinit, "typeOfSecondFixedSurface", grib_ltype2); GRIB_CHECK(my_grib_set_long(gh, "topLevel", (long) dlevel1), 0); GRIB_CHECK(my_grib_set_long(gh, "bottomLevel", (long) dlevel2), 0); } else { grib2DefLevel(gh, gcinit, grib_ltype, grib_ltype2, lbounds, level, dlevel1, dlevel2); } GRIB_CHECK(my_grib_set_long(gh, "NV", 6), 0); int number = 0; cdiInqKeyInt(zaxisID, CDI_GLOBAL, CDI_KEY_NUMBEROFVGRIDUSED, &number); GRIB_CHECK(my_grib_set_long(gh, "numberOfVGridUsed", number), 0); int nlev = 0; cdiInqKeyInt(zaxisID, CDI_GLOBAL, CDI_KEY_NLEV, &nlev); GRIB_CHECK(my_grib_set_long(gh, "nlev", nlev), 0); unsigned char uuid[CDI_UUID_SIZE]; int length = CDI_UUID_SIZE; memset(uuid, 0, length); cdiInqKeyBytes(zaxisID, CDI_GLOBAL, CDI_KEY_UUID, uuid, &length); size_t len = CDI_UUID_SIZE; if ( grib_set_bytes(gh, "uuidOfVGrid", uuid, &len) != 0 ) Warning("Can't write UUID!"); } break; } case ZAXIS_GENERIC: { if ( editionNumber <= 1 ) { grib1DefLevel(gh, gcinit, ltype, lbounds, level, dlevel1, dlevel2); } else { grib2DefLevel(gh, gcinit, ltype, ltype, lbounds, level, dlevel1, dlevel2); } break; } default: { Error("Unsupported zaxis type: %s", zaxisNamePtr(zaxistype)); break; } } } int gribapiGetScanningMode(grib_handle *gh) { long iScansNegatively, jScansPositively, jPointsAreConsecutive; GRIB_CHECK(grib_get_long(gh, "iScansNegatively", &iScansNegatively), 0); GRIB_CHECK(grib_get_long(gh, "jScansPositively", &jScansPositively), 0); GRIB_CHECK(grib_get_long(gh, "jPointsAreConsecutive", &jPointsAreConsecutive), 0); int scanningMode = 128*(bool)iScansNegatively + 64 *(bool)jScansPositively + 32 *(bool)jPointsAreConsecutive; if (cdiDebugExt>=30) printf("gribapiGetScanningMode(): Scanning mode = %02d (%1d%1d%1d)*32; \n",\ scanningMode,(int)jPointsAreConsecutive,(int)jScansPositively,(int)iScansNegatively); return scanningMode; } void gribapiSetScanningMode(grib_handle *gh, int scanningMode) { // 127: reserved for testing; generated test data will be in 64 scanning mode //if (scanningMode== 127) scanningMode = 64; const long iScansNegatively = (scanningMode & 128)/128; const long jScansPositively = (scanningMode & 64)/64; const long jPointsAreConsecutive = (scanningMode & 32)/32; if (cdiDebugExt>=30) { long paramId, levelTypeId, levelId, uvRelativeToGrid; GRIB_CHECK(grib_get_long(gh, "uvRelativeToGrid", &uvRelativeToGrid), 0); GRIB_CHECK(grib_get_long(gh, "indicatorOfParameter", ¶mId), 0); GRIB_CHECK(grib_get_long(gh, "indicatorOfTypeOfLevel", &levelTypeId), 0); GRIB_CHECK(grib_get_long(gh, "level", &levelId), 0); printf("gribapiSetScanningMode(): (param,ltype,level) = (%3d,%3d,%4d); Scanning mode = %02d (%1d%1d%1d)*32; uvRelativeToGrid = %02d\n",\ (int)paramId, (int)levelTypeId, (int)levelId, scanningMode,(int)jPointsAreConsecutive,(int)jScansPositively,(int)iScansNegatively, (int)uvRelativeToGrid); } GRIB_CHECK(my_grib_set_long(gh, "iScansNegatively", iScansNegatively), 0); GRIB_CHECK(my_grib_set_long(gh, "jScansPositively", jScansPositively), 0); GRIB_CHECK(my_grib_set_long(gh, "jPointsAreConsecutive", jPointsAreConsecutive), 0); } /* TABLE 8. SCANNING MODE FLAG (GDS Octet 28) BIT VALUE MEANING 1 0 Points scan in +i direction 1 Points scan in -i direction 2 0 Points scan in -j direction 1 Points scan in +j direction 3 0 Adjacent points in i direction are consecutive (FORTRAN: (I,J)) 1 Adjacent points in j direction are consecutive (FORTRAN: (J,I)) => Scanning Mode 0 0 0 0 0 0 0 0 (00 dec) +i, -j; i direction consecutive (row-major order West->East & North->South) => Scanning Mode 0 1 0 0 0 0 0 0 (64 dec) +i, +j; i direction consecutive (row-major order West->East & South->North ) => Scanning Mode 1 1 0 0 0 0 0 0 (96 dec) +i, +j; j direction consecutive (column-major order South->North & West->East ) NOTE: South->North - As if you would plot the data as image on the screen where [0,0] of the data is the top-left pixel. grib2ppm LAMH_D11_201302150000_00000_oro | display ppm:- ImageMagick (display): [0,0] of an image belongs to the top-left pixel [DEFAULT] : 64 dec iScansNegatively = 0; jScansPositively = 1; jPointsAreConsecutive = 0; => Scanning Mode 64 cdo selindexbox,1,726,100,550 LAMH_D11_201302150000_00000_oro LAMH_D11_201302150000_00000_oro_cropped grib2ppm LAMH_D11_201302150000_00000_oro_cropped | /usr/bin/display ppm:- & # ^^^ this image will be missing the souther parts of data grib2ppm LAMH_D11_201302150000_00000_oro | /usr/bin/display ppm:- & # ^ full domain data */ #ifdef HIRLAM_EXTENSIONS static void verticallyFlipGridDefinitionWhenScanningModeChanged(grib_handle *gh, double yfirst, double ylast, double yinc ) { /* Nj = 550; latitudeOfFirstGridPointInDegrees = -30.8; latitudeOfLastGridPointInDegrees = 24.1; iScansNegatively = 0; jScansPositively = 0; jPointsAreConsecutive = 0; jDirectionIncrementInDegrees = 0.1; When switching from scanning mode 0 <=> 64 yfirst = -30.8 + (550-1)*0.1 yfirst = yfirst + (ysize-1) * yinc yinc = -1.0*yinc */ //long jDim=0; //GRIB_CHECK(grib_get_long(gh, "Nj", &jDim), 0); double latitudeOfFirstGridPointInDegrees; double latitudeOfLastGridPointInDegrees; double jDirectionIncrementInDegrees; //GRIB_CHECK(grib_get_double(gh, "latitudeOfFirstGridPointInDegrees", &latitudeOfFirstGridPointInDegrees), 0); // yfirst //GRIB_CHECK(grib_get_double(gh, "latitudeOfLastGridPointInDegrees", &latitudeOfLastGridPointInDegrees), 0); // ylast //GRIB_CHECK(grib_get_double(gh, "jDirectionIncrementInDegrees", &jDirectionIncrementInDegrees), 0); // yinc if (cdiDebugExt>=10) { Message(" BEFORE: yfirst = %f; ylast = %f; yinc = %f; ", yfirst,ylast, yinc); } GRIB_CHECK(my_grib_set_double(gh, "latitudeOfFirstGridPointInDegrees", ylast), 0); GRIB_CHECK(my_grib_set_double(gh, "latitudeOfLastGridPointInDegrees", yfirst), 0); //yinc *= -1.0; // don't set yinc here ... //GRIB_CHECK(my_grib_set_double(gh, "jDirectionIncrementInDegrees", yinc), 0); if (cdiDebugExt>=10) { GRIB_CHECK(grib_get_double(gh, "latitudeOfFirstGridPointInDegrees", &latitudeOfFirstGridPointInDegrees), 0); // yfirst GRIB_CHECK(grib_get_double(gh, "latitudeOfLastGridPointInDegrees", &latitudeOfLastGridPointInDegrees), 0); // ylast GRIB_CHECK(grib_get_double(gh, "jDirectionIncrementInDegrees", &jDirectionIncrementInDegrees), 0); // yinc Message("CHANGED INTO: yfirst = %f, ylast = %f, yinc = %f",latitudeOfFirstGridPointInDegrees,latitudeOfLastGridPointInDegrees, jDirectionIncrementInDegrees); } } static void convertDataScanningMode(int scanModeIN, int scanModeOUT, double *data, size_t gridsize, size_t iDim, size_t jDim) { size_t i,j; size_t idxIN, idxOUT; // 127: reserved for testing; it will generate test data in 64 scanning mode if (scanModeOUT== 127) // fill with testdata ... { scanModeOUT = 64; if (cdiDebugExt>=30) printf("convertDataScanningMode(): Generating test data in 64 scanning mode..\n"); for (j=0; j=30) printf("convertDataScanningMode(): ERROR: (iDim*jDim)!= gridsize; (%zu * %zu) != %zu\n", iDim,jDim, gridsize); return; } if (cdiDebugExt>=30) printf("convertDataScanningMode(): scanModeIN=%02d => scanModeOUT=%02d ; where: (iDim * jDim == gridsize) (%zu*%zu == %zu)\n",scanModeIN, scanModeOUT, iDim,jDim, gridsize); if (cdiDebugExt>=100) { printf("convertDataScanningMode(): data IN:\n"); for (j=0; j=30) printf("convertDataScanningMode(): INFO: Nothing to do; scanModeIN==scanModeOUT..\n"); return; } if (0) { return; if (scanModeOUT==00) { if (cdiDebugExt>0) printf("convertDataScanningMode(): Leave data unchaged BUT set scanModeOUT=00.\n"); // CHECK: Looks like that GRIB-API provide (no matter what) data in the scannning mode 00, even it is store in the gribfile as 64 !! return; } } double *dataCopy = (double *) malloc(gridsize*sizeof(double)); memcpy((void*)dataCopy,(void*) data, gridsize*sizeof(double)); if (scanModeIN==64) // Scanning Mode (00 dec) +i, -j; i direction consecutive (row-major order West->East & South->North ) { // Scanning Mode (64 dec) +i, +j; i direction consecutive (row-major order West->East & North->South ) // Scanning Mode (96 dec) +i, +j; j direction consecutive (column-major order North->South & West->East ) if (scanModeOUT==00) // CHECK: Looks like that GRIB-API provide (no matter what) data in the scannning mode 00, even it is store in the gribfile as 64 !! #define VERTICAL_FLIP #ifdef VERTICAL_FLIP { // flip the data vertically .. idxIN= 0; idxOUT= (jDim-1)*iDim; if (cdiDebugExt>=30) printf("convertDataScanningMode(): copying rows nr. (%04d : %04zu)\n",0,jDim-1); for (j=0; j=30) printf("convertDataScanningMode(): copying columns nr. (%04d : %04d);\n", 0, iDim-1); for (i=0; i%03d] = %f;",idxIN,idxOUT,dataCopy[idxIN]); data[idxOUT] = dataCopy[idxIN]; } } } } // end if (scanModeOUT==00) #endif if (scanModeOUT==96) { // transpose the data if (cdiDebugExt>=30) printf("convertDataScanningMode(): transpose data rows=>columns nr. (%04d : %04zu) => (%04d : %04zu);\n", 0, iDim-1, 0, jDim-1); for (j=0; j%03d] = %f;",idxIN,idxOUT,dataCopy[idxIN]); data[idxOUT] = dataCopy[idxIN]; } //printf(".\n"); } } // end if (scanModeOUT==96) } // end if (scanModeIN==64) if (scanModeIN==00) // Scanning Mode (00 dec) +i, -j; i direction consecutive (row-major order West->East & South->North ) { // Scanning Mode (64 dec) +i, +j; i direction consecutive (row-major order West->East & North->South ) // Scanning Mode (96 dec) +i, +j; j direction consecutive (column-major order North->South & West->East ) if (scanModeOUT==64) { // flip the data vertically .. idxIN= 0; idxOUT= (jDim-1)*iDim; for (j=0; j=25) printf("convertDataScanningMode(): copying row nr. %04zu; [idxIN=%08zu] => [idxOUT=%08zu]\n",j, idxIN, idxOUT); memcpy((void*)&data[idxOUT], (void*)&dataCopy[idxIN], iDim*sizeof(double)); idxIN += iDim; idxOUT -= iDim; } } // end if (scanModeOUT==64) if (scanModeOUT==96) { // transpose the data size_t jInv; for (j=0; j=30) printf("convertDataScanningMode(): processing row nr. %04zu;\n", j); jInv = (jDim-1) -j; for (i=0; iEast & South->North ) { // Scanning Mode (64 dec) +i, +j; i direction consecutive (row-major order West->East & North->South ) // Scanning Mode (96 dec) +i, +j; j direction consecutive (column-major order North->South & West->East ) if (scanModeOUT==64) { // transpose the data for (j=0; j=30) printf("convertDataScanningMode(): processing row nr. %04zu;\n", j); size_t jXiDim = j*iDim; for (i=0; i=30) printf("convertDataScanningMode(): processing row nr. %04zu;\n", j); jInv = (jDim-1) -j; size_t jXiDim = j*iDim; for (i=0; i=100) { printf("convertDataScanningMode(): data OUT (new scanning mode):\n"); for (j=0; j=100) { size_t gridsize = gridInqSize(gridID); Message("(scanModeIN=%d; gridsize=%zu", scanModeIN, gridsize); } if ( cdiGribDataScanningMode.active ) // allowed modes: <0, 64, 96>; Default is 64 { size_t iDim = gridInqXsize(gridID); size_t jDim = gridInqYsize(gridID); double yfirst = gridInqYval(gridID, 0); double ylast = gridInqYval(gridID, jDim-1); double yinc = gridInqYinc(gridID); int scanModeOUT = cdiGribDataScanningMode.value; convertDataScanningMode(scanModeIN, scanModeOUT, (double*)data, datasize, iDim, jDim); // This will overrule the old scanning mode of the given grid if (cdiDebugExt>=10) Message("Set GribDataScanningMode (%d) => (%d)", scanModeIN, cdiGribDataScanningMode.value); gribapiSetScanningMode(gh, cdiGribDataScanningMode.value); if (((scanModeIN==00) && (cdiGribDataScanningMode.value==64)) || ((scanModeIN==64) && (cdiGribDataScanningMode.value==00)) ) verticallyFlipGridDefinitionWhenScanningModeChanged(gh, yfirst, ylast, yinc); } else { if (cdiDebugExt>=100) Message("Set GribDataScanningMode => (%d) based on used grid", scanModeIN); gribapiSetScanningMode(gh, scanModeIN); } #endif } } /* #define GRIBAPIENCODETEST 1 */ size_t gribapiEncode(int varID, int levelID, int vlistID, int gridID, int zaxisID, int64_t vdate, int vtime, int tsteptype, int numavg, size_t datasize, const double *data, size_t nmiss, void **gribbuffer, size_t *gribbuffersize, int comptype, void *gribContainer) { void *dummy = NULL; /* int ensID, ensCount, forecast_type; *//* Ensemble Data */ long editionNumber = 2; // extern unsigned char _grib_template_GRIB2[]; cdi_check_gridsize_int_limit("GRIB", datasize); int param = vlistInqVarParam(vlistID, varID); int datatype = vlistInqVarDatatype(vlistID, varID); int typeOfGeneratingProcess = 0; cdiInqKeyInt(vlistID, varID, CDI_KEY_TYPEOFGENERATINGPROCESS, &typeOfGeneratingProcess); int productDefinitionTemplate = 0; cdiInqKeyInt(vlistID, varID, CDI_KEY_PRODUCTDEFINITIONTEMPLATE, &productDefinitionTemplate); int uvRelativeToGrid = -1; cdiInqKeyInt(vlistID, varID, CDI_KEY_UVRELATIVETOGRID, &uvRelativeToGrid); char name[256], stdname[256]; vlistInqVarName(vlistID, varID, name); vlistInqVarStdname(vlistID, varID, stdname); #ifdef GRIBAPIENCODETEST grib_handle *gh = (grib_handle *) gribHandleNew(editionNumber); #else gribContainer_t *gc = (gribContainer_t *) gribContainer; assert(gc != NULL); grib_handle *gh = (struct grib_handle *) gc->gribHandle; #endif GRIB_CHECK(grib_get_long(gh, "editionNumber", &editionNumber), 0); if ( editionNumber == 2 ) { if ( ! gc->init ) { int backgroundProcess = 0; cdiInqKeyInt(vlistID, varID, CDI_KEY_BACKGROUNDPROCESS, &backgroundProcess); GRIB_CHECK(my_grib_set_long(gh, "typeOfGeneratingProcess", typeOfGeneratingProcess), 0); GRIB_CHECK(my_grib_set_long(gh, "backgroundProcess", backgroundProcess), 0); int status, tablesVersion, localTablesVersion; status = cdiInqKeyInt(vlistID, varID, CDI_KEY_TABLESVERSION, &tablesVersion); if ( status == 0 ) GRIB_CHECK(my_grib_set_long(gh, "tablesVersion", (long)tablesVersion), 0); status = cdiInqKeyInt(vlistID, varID, CDI_KEY_LOCALTABLESVERSION, &localTablesVersion); if ( status == 0 ) GRIB_CHECK(my_grib_set_long(gh, "localTablesVersion", (long)localTablesVersion), 0); int typeOfProcessedData = 0; status = cdiInqKeyInt(vlistID, varID, CDI_KEY_TYPEOFPROCESSEDDATA, &typeOfProcessedData); if ( status == 0 ) GRIB_CHECK(my_grib_set_long(gh, "typeOfProcessedData", (long)typeOfProcessedData), 0); /* int constituentType = 0; status = cdiInqKeyInt(vlistID, varID, CDI_KEY_CONSTITUENTTYPE, &constituentType); if ( status == 0 ) GRIB_CHECK(my_grib_set_long(gh, "constituentType", (long)constituentType), 0); */ } } gribapiDefTime((int)editionNumber, productDefinitionTemplate, typeOfGeneratingProcess, gh, vdate, vtime, tsteptype, numavg, vlistInqTaxis(vlistID), gc->init); { int typeOfTimeIncrement = 0; int status = cdiInqKeyInt(vlistID, varID, CDI_KEY_TYPEOFTIMEINCREMENT, &typeOfTimeIncrement); if ( status == 0 ) GRIB_CHECK(my_grib_set_long(gh, "typeOfTimeIncrement", (long)typeOfTimeIncrement), 0); } { int status, perturbationNumber, numberOfForecastsInEnsemble, typeOfEnsembleForecast; status = cdiInqKeyInt(vlistID, varID, CDI_KEY_TYPEOFENSEMBLEFORECAST, &typeOfEnsembleForecast); if ( status == 0 ) grib_set_long(gh, "typeOfEnsembleForecast", typeOfEnsembleForecast); status = cdiInqKeyInt(vlistID, varID, CDI_KEY_NUMBEROFFORECASTSINENSEMBLE, &numberOfForecastsInEnsemble); if ( status == 0 ) grib_set_long(gh, "numberOfForecastsInEnsemble", numberOfForecastsInEnsemble); status = cdiInqKeyInt(vlistID, varID, CDI_KEY_PERTURBATIONNUMBER, &perturbationNumber); if ( status == 0 ) grib_set_long(gh, "perturbationNumber", perturbationNumber); } if ( ! gc->init ) gribapiDefInstitut(gh, vlistID, varID); if ( ! gc->init ) gribapiDefModel(gh, vlistID, varID); if ( ! gc->init ) gribapiDefParam((int)editionNumber, gh, param, name, stdname); if ( ! gc->init && editionNumber == 2 ) { int shapeOfTheEarth = 0; cdiInqKeyInt(vlistID, varID, CDI_KEY_SHAPEOFTHEEARTH, &shapeOfTheEarth); GRIB_CHECK(my_grib_set_long(gh, "shapeOfTheEarth", (long)shapeOfTheEarth), 0); int grib2LocalSectionNumber, section2PaddingLength; int mpimType, mpimClass, mpimUser; if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_MPIMTYPE, &mpimType) == CDI_NOERR && cdiInqKeyInt(vlistID, varID, CDI_KEY_MPIMCLASS, &mpimClass) == CDI_NOERR && cdiInqKeyInt(vlistID, varID, CDI_KEY_MPIMUSER, &mpimUser) == CDI_NOERR ) { grib_set_long(gh, "grib2LocalSectionPresent", 1); grib_set_long(gh, "grib2LocalSectionNumber", 1); grib_set_long(gh, "mpimType", mpimType); grib_set_long(gh, "mpimClass", mpimClass); grib_set_long(gh, "mpimUser", mpimUser); int revNumLen = 20; unsigned char revNumber[revNumLen]; if ( cdiInqKeyBytes(vlistID, varID, CDI_KEY_REVNUMBER, revNumber, &revNumLen) == CDI_NOERR ) { size_t revNumLenS = revNumLen; grib_set_bytes(gh, "revNumber", revNumber, &revNumLenS); } int revStatus; if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_REVSTATUS, &revStatus) == CDI_NOERR ) grib_set_long(gh, "revStatus", revStatus); } else if ( cdiInqKeyInt(vlistID, varID, CDI_KEY_GRIB2LOCALSECTIONNUMBER, &grib2LocalSectionNumber) == CDI_NOERR && cdiInqKeyInt(vlistID, varID, CDI_KEY_SECTION2PADDINGLENGTH, §ion2PaddingLength) == CDI_NOERR ) { grib_set_long(gh, "grib2LocalSectionPresent", 1); grib_set_long(gh, "grib2LocalSectionNumber", grib2LocalSectionNumber); unsigned char *section2Padding = (unsigned char*) Malloc(section2PaddingLength); cdiInqKeyBytes(vlistID, varID, CDI_KEY_SECTION2PADDING, section2Padding, §ion2PaddingLength); size_t len = section2PaddingLength; grib_set_bytes(gh, "section2Padding", section2Padding, &len); Free(section2Padding); } } // bitsPerValue have to be defined first (complex packing) GRIB_CHECK(my_grib_set_long(gh, "bitsPerValue", (long)grbBitsPerValue(datatype)), 0); if ( ! gc->init ) gribapiDefGrid((int)editionNumber, gh, gridID, comptype, datatype, uvRelativeToGrid); gribapiDefLevel((int)editionNumber, gh, zaxisID, levelID, gc->init, productDefinitionTemplate); vlist_t *vlistptr = vlist_to_pointer(vlistID); //if (!gc->init) { int ret = 0; /* NOTE: Optional key/value pairs: Note that we do not distinguish between tiles here! */ for ( int i=0; ivars[varID].opt_grib_nentries; i++ ) { if ( vlistptr->vars[varID].opt_grib_kvpair[i].update ) { //DR: Fix for multi-level fields (otherwise only the 1st level is correct) if ( zaxisInqSize(zaxisID)==(levelID+1) ) vlistptr->vars[varID].opt_grib_kvpair[i].update = false; if (vlistptr->vars[varID].opt_grib_kvpair[i].data_type == t_double) { if ( CDI_Debug ) Message("key \"%s\" : double value = %g\n", vlistptr->vars[varID].opt_grib_kvpair[i].keyword, vlistptr->vars[varID].opt_grib_kvpair[i].dbl_val); my_grib_set_double(gh, vlistptr->vars[varID].opt_grib_kvpair[i].keyword, vlistptr->vars[varID].opt_grib_kvpair[i].dbl_val); GRIB_CHECK(ret, 0); } if (vlistptr->vars[varID].opt_grib_kvpair[i].data_type == t_int) { if ( CDI_Debug ) Message("key \"%s\" : integer value = %d\n", vlistptr->vars[varID].opt_grib_kvpair[i].keyword, vlistptr->vars[varID].opt_grib_kvpair[i].int_val); my_grib_set_long(gh, vlistptr->vars[varID].opt_grib_kvpair[i].keyword, (long) vlistptr->vars[varID].opt_grib_kvpair[i].int_val); GRIB_CHECK(ret, 0); } } } } if ( nmiss > 0 ) { GRIB_CHECK(my_grib_set_long(gh, "bitmapPresent", 1), 0); GRIB_CHECK(my_grib_set_double(gh, "missingValue", vlistInqVarMissval(vlistID, varID)), 0); } gribapiSetExtMode(gh, gridID, datasize, data); GRIB_CHECK(grib_set_double_array(gh, "values", data, datasize), 0); /* get the size of coded message */ size_t recsize = 0; GRIB_CHECK(grib_get_message(gh, (const void **)&dummy, &recsize), 0); recsize += 512; /* add some space for possible filling */ *gribbuffersize = recsize; *gribbuffer = Malloc(*gribbuffersize); if ( ! gc->init && editionNumber == 2 ) { long pdis; grib_get_long(gh, "discipline", &pdis); if ( pdis != 255 ) { char cdi_name[CDI_MAX_NAME]; cdi_name[0] = 0; vlistInqVarName(vlistID, varID, cdi_name); char grb_name[256]; gribapiGetString(gh, "shortName", grb_name, sizeof(grb_name)); strToLower(cdi_name); strToLower(grb_name); bool checkName = (!grb_name[0] && strncmp(cdi_name, "param", 5) == 0) ? false : true; if ( checkName && ((strlen(cdi_name) != strlen(grb_name)) || !strStartsWith(cdi_name, grb_name)) ) Message("*** GRIB2 shortName does not correspond to chosen variable name: \"%s\" (\"%s\").", grb_name[0]?grb_name:"unknown", cdi_name); } } /* get a copy of the coded message */ GRIB_CHECK(grib_get_message_copy(gh, *gribbuffer, &recsize), 0); #ifdef GRIBAPIENCODETEST gribHandleDelete(gh); #endif gc->init = true; return recsize; } void gribapiChangeParameterIdentification(grib_handle *gh, int code, int ltype, int level) { // timeRangeIndicator: could be included later if (code!=-1) GRIB_CHECK(my_grib_set_long(gh, "indicatorOfParameter", code), 0); if (ltype!=-1) GRIB_CHECK(my_grib_set_long(gh, "indicatorOfTypeOfLevel", ltype), 0); if (level!=-1) GRIB_CHECK(my_grib_set_long(gh, "level", level), 0); } #endif /* * Local Variables: * c-file-style: "Java" * c-basic-offset: 2 * indent-tabs-mode: nil * show-trailing-whitespace: t * require-trailing-newline: t * End: */