/* * (C) Copyright 2005- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * * In applying this licence, ECMWF does not waive the privileges and immunities granted to it by * virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction. */ #include "grib_api_internal.h" #include #include #include typedef enum { eROUND_ANGLE_UP, eROUND_ANGLE_DOWN } RoundingPolicy; static void set_total_length(unsigned char* buffer, long* section_length, const long* section_offset, int edition, size_t totalLength) { long off; switch (edition) { case 1: if (totalLength < 0x800000) { off = 32; grib_encode_unsigned_long(buffer, (unsigned long)totalLength, &off, 24); } else { long s4len, t120; totalLength -= 4; t120 = (totalLength + 119) / 120; s4len = t120 * 120 - totalLength; totalLength = 0x800000 | t120; off = 32; grib_encode_unsigned_long(buffer, (unsigned long)totalLength, &off, 24); off = section_offset[4] * 8; grib_encode_unsigned_long(buffer, (unsigned long)s4len, &off, 24); } break; case 2: off = 64; grib_encode_unsigned_long(buffer, (unsigned long)totalLength, &off, 64); break; } } static grib_handle* grib_sections_copy_internal(grib_handle* hfrom, grib_handle* hto, int sections[], int* err) { int i; size_t totalLength = 0; unsigned char* buffer; unsigned char* p; long edition = 0; long section_length[MAX_NUM_SECTIONS] = {0,}; long section_offset[MAX_NUM_SECTIONS] = {0,}; long off = 0; grib_handle* h; char section_length_str[64] = "section0Length"; char section_offset_str[64] = "offsetSection0"; long length, offset; *err = grib_get_long(hfrom, "edition", &edition); if (*err) return NULL; for (i = 0; i <= hfrom->sections_count; i++) { if (sections[i]) { h = hfrom; } else { h = hto; } snprintf(section_length_str, sizeof(section_length_str), "section%dLength", i); if (grib_get_long(h, section_length_str, &length)) continue; section_length[i] = length; snprintf(section_offset_str, sizeof(section_offset_str), "offsetSection%d", i); if (grib_get_long(h, section_offset_str, &offset)) continue; section_offset[i] = offset; totalLength += section_length[i]; } buffer = (unsigned char*)grib_context_malloc_clear(hfrom->context, totalLength * sizeof(char)); p = buffer; off = 0; for (i = 0; i <= hfrom->sections_count; i++) { const grib_handle* hand = NULL; if (sections[i]) hand = hfrom; else hand = hto; p = (unsigned char*)memcpy(p, hand->buffer->data + section_offset[i], section_length[i]); section_offset[i] = off; off += section_length[i]; p += section_length[i]; } // copy section 3 present flag if (edition == 1) { const void* buffer_to = NULL; size_t size_to = 0; grib_get_message(hto, &buffer_to, &size_to); memcpy(buffer + 15, ((unsigned char*)buffer_to) + 15, 1); } set_total_length(buffer, section_length, section_offset, edition, totalLength); h = grib_handle_new_from_message(hfrom->context, buffer, totalLength); // to allow freeing of buffer h->buffer->property = CODES_MY_BUFFER; switch (edition) { case 1: if (sections[1] && sections[2]) break; if (sections[1]) { long PVPresent; grib_get_long(hfrom, "PVPresent", &PVPresent); if (PVPresent) { double* pv; long numberOfVerticalCoordinateValues; size_t size = 0; grib_get_long(hfrom, "numberOfVerticalCoordinateValues", &numberOfVerticalCoordinateValues); size = numberOfVerticalCoordinateValues; pv = (double*)grib_context_malloc_clear(hfrom->context, numberOfVerticalCoordinateValues * sizeof(double)); grib_get_double_array(hfrom, "pv", pv, &size); grib_set_long(h, "PVPresent", 1); grib_set_double_array(h, "pv", pv, size); grib_context_free(hfrom->context, pv); } else { grib_set_long(h, "PVPresent", 0); } } if (sections[2]) { long PVPresent; grib_get_long(hto, "PVPresent", &PVPresent); if (PVPresent) { double* pv; long numberOfVerticalCoordinateValues; size_t size = 0; grib_get_long(hto, "numberOfVerticalCoordinateValues", &numberOfVerticalCoordinateValues); size = numberOfVerticalCoordinateValues; pv = (double*)grib_context_malloc_clear(hto->context, numberOfVerticalCoordinateValues * sizeof(double)); grib_get_double_array(hto, "pv", pv, &size); grib_set_long(h, "PVPresent", 1); grib_set_double_array(h, "pv", pv, size); grib_context_free(hto->context, pv); } else { grib_set_long(h, "PVPresent", 0); } } break; case 2: if (sections[1]) { long discipline; grib_get_long(hfrom, "discipline", &discipline); grib_set_long(h, "discipline", discipline); } break; } return h; } grib_handle* grib_util_sections_copy(grib_handle* hfrom, grib_handle* hto, int what, int* err) { long edition_from = 0; long edition_to = 0; long localDefinitionNumber = -1; int sections_to_copy[MAX_NUM_SECTIONS] = {0,}; *err = grib_get_long(hfrom, "edition", &edition_from); if (*err) return NULL; *err = grib_get_long(hto, "edition", &edition_to); if (*err) return NULL; if (hfrom->context->debug) { fprintf(stderr, "ECCODES DEBUG %s: Copying the following sections: ", __func__); if (what & GRIB_SECTION_GRID) fprintf(stderr, "Grid, "); if (what & GRIB_SECTION_PRODUCT) fprintf(stderr, "Product, "); if (what & GRIB_SECTION_LOCAL) fprintf(stderr, "Local, "); if (what & GRIB_SECTION_DATA) fprintf(stderr, "Data, "); if (what & GRIB_SECTION_BITMAP) fprintf(stderr, "Bitmap, "); fprintf(stderr, "\n"); } if (edition_to != 1 && edition_to != 2) { *err = GRIB_NOT_IMPLEMENTED; return NULL; } if (edition_from != edition_to) { *err = GRIB_DIFFERENT_EDITION; return NULL; } if (what & GRIB_SECTION_GRID) { switch (edition_from) { case 1: sections_to_copy[2] = 1; break; case 2: sections_to_copy[3] = 1; break; } } if (what & GRIB_SECTION_DATA) { switch (edition_from) { case 1: sections_to_copy[3] = 1; sections_to_copy[4] = 1; break; case 2: sections_to_copy[5] = 1; sections_to_copy[6] = 1; sections_to_copy[7] = 1; break; } } if (what & GRIB_SECTION_LOCAL) { switch (edition_from) { case 1: sections_to_copy[1] = 1; break; case 2: sections_to_copy[2] = 1; break; } } if (what & GRIB_SECTION_PRODUCT) { switch (edition_from) { case 1: grib_get_long(hfrom, "localDefinitionNumber", &localDefinitionNumber); if (localDefinitionNumber == 13) { sections_to_copy[4] = 1; } sections_to_copy[1] = 1; break; case 2: sections_to_copy[1] = 1; sections_to_copy[4] = 1; break; } } if (what & GRIB_SECTION_BITMAP) { switch (edition_from) { case 1: sections_to_copy[3] = 1; break; case 2: sections_to_copy[6] = 1; break; } } return grib_sections_copy_internal(hfrom, hto, sections_to_copy, err); } static grib_trie* init_list(const char* name); static grib_trie* param_id_list = NULL; static grib_trie* mars_param_list = NULL; grib_string_list* grib_util_get_param_id(const char* mars_param) { if (!mars_param_list && (mars_param_list = init_list("mars_param.table")) == NULL) return NULL; return (grib_string_list*)grib_trie_get(mars_param_list, mars_param); } grib_string_list* grib_util_get_mars_param(const char* param_id) { if (!param_id_list && (param_id_list = init_list("param_id.table")) == NULL) return NULL; return (grib_string_list*)grib_trie_get(param_id_list, param_id); } static grib_trie* init_list(const char* name) { char* full_path = 0; FILE* fh; char s[101]; char param[101]; grib_string_list* list = 0; grib_string_list* next = 0; grib_trie* trie_list; grib_context* c = grib_context_get_default(); full_path = grib_context_full_defs_path(c, name); fh = codes_fopen(full_path, "r"); if (!fh) { grib_context_log(c, GRIB_LOG_PERROR, "unable to read %s", full_path); return NULL; } list = (grib_string_list*)grib_context_malloc_clear(c, sizeof(grib_string_list)); trie_list = grib_trie_new(c); if (fscanf(fh, "%100s", param) == EOF) { fclose(fh); return NULL; } while (fscanf(fh, "%100s", s) != EOF) { if (!strcmp(s, "|")) { grib_trie_insert(trie_list, param, list); if (fscanf(fh, "%100s", param) == EOF) { fclose(fh); return trie_list; } list = NULL; } else { if (!list) { list = (grib_string_list*)grib_context_malloc_clear(c, sizeof(grib_string_list)); list->value = grib_context_strdup(c, s); } else { next = list; while (next->next) next = next->next; next->next = (grib_string_list*)grib_context_malloc_clear(c, sizeof(grib_string_list)); next->next->value = grib_context_strdup(c, s); } } } fclose(fh); return 0; } static const char* get_packing_spec_packing_name(long packing_spec_packing) { if (GRIB_UTIL_PACKING_USE_PROVIDED == packing_spec_packing) return "GRIB_UTIL_PACKING_USE_PROVIDED"; if (GRIB_UTIL_PACKING_SAME_AS_INPUT == packing_spec_packing) return "GRIB_UTIL_PACKING_SAME_AS_INPUT"; ECCODES_ASSERT(!"get_packing_spec_packing_name: invalid packing"); return NULL; } static const char* get_packing_spec_packing_type_name(long packing_spec_packing_type) { if (GRIB_UTIL_PACKING_TYPE_SAME_AS_INPUT == packing_spec_packing_type) return "GRIB_UTIL_PACKING_TYPE_SAME_AS_INPUT"; if (GRIB_UTIL_PACKING_TYPE_SPECTRAL_COMPLEX == packing_spec_packing_type) return "GRIB_UTIL_PACKING_TYPE_SPECTRAL_COMPLEX"; if (GRIB_UTIL_PACKING_TYPE_SPECTRAL_SIMPLE == packing_spec_packing_type) return "GRIB_UTIL_PACKING_TYPE_SPECTRAL_SIMPLE"; if (GRIB_UTIL_PACKING_TYPE_JPEG == packing_spec_packing_type) return "GRIB_UTIL_PACKING_TYPE_JPEG"; if (GRIB_UTIL_PACKING_TYPE_GRID_COMPLEX == packing_spec_packing_type) return "GRIB_UTIL_PACKING_TYPE_GRID_COMPLEX"; if (GRIB_UTIL_PACKING_TYPE_GRID_SIMPLE == packing_spec_packing_type) return "GRIB_UTIL_PACKING_TYPE_GRID_SIMPLE"; if (GRIB_UTIL_PACKING_TYPE_GRID_SIMPLE_MATRIX == packing_spec_packing_type) return "GRIB_UTIL_PACKING_TYPE_GRID_SIMPLE_MATRIX"; if (GRIB_UTIL_PACKING_TYPE_GRID_SECOND_ORDER == packing_spec_packing_type) return "GRIB_UTIL_PACKING_TYPE_GRID_SECOND_ORDER"; if (GRIB_UTIL_PACKING_TYPE_CCSDS == packing_spec_packing_type) return "GRIB_UTIL_PACKING_TYPE_CCSDS"; if (GRIB_UTIL_PACKING_TYPE_IEEE == packing_spec_packing_type) return "GRIB_UTIL_PACKING_TYPE_IEEE"; ECCODES_ASSERT(!"get_packing_spec_packing_type_name: invalid packing_type"); return NULL; } // For debugging purposes static void print_values(const grib_context* c, const grib_util_grid_spec* spec, const grib_util_packing_spec* packing_spec, const char* input_packing_type, const double* data_values, const size_t data_values_count, // the data pay load const grib_values* keyval_pairs, const size_t count) // keys and their values { size_t i = 0; int isConstant = 1; double v = 0, minVal = DBL_MAX, maxVal = -DBL_MAX; fprintf(stderr, "ECCODES DEBUG grib_util: input_packing_type = %s\n", input_packing_type); fprintf(stderr, "ECCODES DEBUG grib_util: grib_set_values, setting %zu key/value pairs\n", count); for (i = 0; i < count; i++) { switch (keyval_pairs[i].type) { case GRIB_TYPE_LONG: fprintf(stderr, "ECCODES DEBUG grib_util: => %s = %ld;\n", keyval_pairs[i].name, keyval_pairs[i].long_value); break; case GRIB_TYPE_DOUBLE: fprintf(stderr, "ECCODES DEBUG grib_util: => %s = %.16e;\n", keyval_pairs[i].name, keyval_pairs[i].double_value); break; case GRIB_TYPE_STRING: fprintf(stderr, "ECCODES DEBUG grib_util: => %s = \"%s\";\n", keyval_pairs[i].name, keyval_pairs[i].string_value); break; } } fprintf(stderr, "ECCODES DEBUG grib_util: data_values_count=%zu;\n", data_values_count); for (i = 0; i < data_values_count; i++) { if (i == 0) v = data_values[i]; if (data_values[i] != spec->missingValue) { if (v == spec->missingValue) { v = data_values[i]; } else if (v != data_values[i]) { isConstant = 0; break; } } } for (i = 0; i < data_values_count; i++) { v = data_values[i]; if (v != spec->missingValue) { if (v < minVal) minVal = v; if (v > maxVal) maxVal = v; } } fprintf(stderr, "ECCODES DEBUG grib_util: data_values are CONSTANT? %d\t(min=%.16e, max=%.16e)\n", isConstant, minVal, maxVal); if (c->gribex_mode_on) fprintf(stderr, "ECCODES DEBUG grib_util: GRIBEX mode is turned on!\n"); fprintf(stderr, "ECCODES DEBUG grib_util: packing_spec->editionNumber = %ld\n", packing_spec->editionNumber); fprintf(stderr, "ECCODES DEBUG grib_util: packing_spec->packing = %s\n", get_packing_spec_packing_name(packing_spec->packing)); fprintf(stderr, "ECCODES DEBUG grib_util: packing_spec->packing_type = %s\n", get_packing_spec_packing_type_name(packing_spec->packing_type)); // if (spec->bitmapPresent) { // int missing = 0; // size_t j = 0; // double min = 1e100; // for(j = 0; j < data_values_count ; j++) // { // double d = data_values[j] - spec->missingValue; // if(d < 0) d = -d; // if(d < min) { // min = d; // } // if(data_values[j] == spec->missingValue) // missing++; // } // } } // static int DBL_EQUAL(double d1, double d2, double tolerance) // { // return fabs(d1-d2) < tolerance; // } // Returns a boolean: 1 if angle can be encoded, 0 otherwise // static int grib1_angle_can_be_encoded(const double angle) // { // const double angle_milliDegrees = angle * 1000; // double rounded = (int)(angle_milliDegrees+0.5)/1000.0; // if (angle<0) { // rounded = (int)(angle_milliDegrees-0.5)/1000.0; // } // if (angle == rounded) return 1; // return 0; // sub millidegree. Cannot be encoded in grib1 // } // Returns a boolean: 1 if angle can be encoded, 0 otherwise // static int angle_can_be_encoded(const double angle, const double angular_precision) // { // const double angle_expanded = angle * angular_precision; // ECCODES_ASSERT(angular_precision>0); // double rounded = (long)(angle_expanded+0.5)/angular_precision; // if (angle<0) { // rounded = (long)(angle_expanded-0.5)/angular_precision; // } // if (angle == rounded) return 1; // //printf(" ......... angle cannot be encoded: %.10e\n", angle); // return 0; // Cannot be encoded // } // Returns a boolean: 1 if angle can be encoded, 0 otherwise static int angle_can_be_encoded(const grib_handle* h, const double angle) { int ret = 0; int retval = 1; grib_handle* h2 = NULL; char sample_name[16] = {0,}; long angle_subdivisions = 0; // e.g. 1e3 for grib1 and 1e6 for grib2 long edition = 0, coded = 0; double expanded, diff; if ((ret = grib_get_long(h, "edition", &edition)) != 0) return ret; if ((ret = grib_get_long(h, "angleSubdivisions", &angle_subdivisions)) != 0) return ret; ECCODES_ASSERT(angle_subdivisions > 0); snprintf(sample_name, sizeof(sample_name), "GRIB%ld", edition); h2 = grib_handle_new_from_samples(0, sample_name); if ((ret = grib_set_double(h2, "latitudeOfFirstGridPointInDegrees", angle)) != 0) return ret; if ((ret = grib_get_long(h2, "latitudeOfFirstGridPoint", &coded)) != 0) return ret; grib_handle_delete(h2); expanded = angle * angle_subdivisions; diff = fabs(expanded - coded); if (diff < 1.0 / angle_subdivisions) retval = 1; else retval = 0; return retval; } static double adjust_angle(const double angle, const RoundingPolicy policy, const double angle_subdivisions) { double result = 0; ECCODES_ASSERT(angle_subdivisions > 0); result = angle * angle_subdivisions; if (policy == eROUND_ANGLE_UP) result = round(result + 0.5); else result = round(result - 0.5); result = result / angle_subdivisions; return result; } // Search key=value array for: // * latitudeOfFirstGridPointInDegrees // * longitudeOfFirstGridPointInDegrees // * latitudeOfLastGridPointInDegrees // * longitudeOfLastGridPointInDegrees // and change their values to expand the bounding box static int expand_bounding_box(const grib_handle* h, grib_values* values, const size_t count) { int ret = GRIB_SUCCESS; size_t i = 0; double new_angle = 0; RoundingPolicy roundingPolicy = eROUND_ANGLE_UP; long angle_subdivisions = 0; // e.g. 1e3 for grib1 and 1e6 for grib2 if ((ret = grib_get_long(h, "angleSubdivisions", &angle_subdivisions)) != 0) return ret; for (i = 0; i < count; i++) { int is_angle = 0; if (strcmp(values[i].name, "longitudeOfFirstGridPointInDegrees") == 0) { roundingPolicy = eROUND_ANGLE_DOWN; is_angle = 1; } else if (strcmp(values[i].name, "longitudeOfLastGridPointInDegrees") == 0) { roundingPolicy = eROUND_ANGLE_UP; is_angle = 1; } else if (strcmp(values[i].name, "latitudeOfFirstGridPointInDegrees") == 0) { roundingPolicy = eROUND_ANGLE_UP; is_angle = 1; } else if (strcmp(values[i].name, "latitudeOfLastGridPointInDegrees") == 0) { roundingPolicy = eROUND_ANGLE_DOWN; is_angle = 1; } if (is_angle && !angle_can_be_encoded(h, values[i].double_value)) { new_angle = adjust_angle(values[i].double_value, roundingPolicy, angle_subdivisions); if (h->context->debug) { fprintf(stderr, "ECCODES DEBUG grib_util: EXPAND_BOUNDING_BOX %s: old=%.15e new=%.15e (%s)\n", values[i].name, values[i].double_value, new_angle, (roundingPolicy == eROUND_ANGLE_UP ? "Up" : "Down")); } values[i].double_value = new_angle; } } return ret; } /* Returns a boolean: 1 if angle is too small, 0 otherwise */ /*static int angle_too_small(const double angle, const double angular_precision) { const double a = fabs(angle); if (a > 0 && a < angular_precision) return 1; return 0; } static double normalise_angle(double angle) { while (angle<0) angle += 360; while (angle>360) angle -= 360; return angle; } static int check_values(const double* data_values, size_t data_values_count) { size_t i = 0; for (i=0; i= DBL_MAX || val <= -DBL_MAX || isnan(val) ) { fprintf(stderr,"GRIB_UTIL_SET_SPEC: Invalid data value: i=%lu, val=%g\n",i, val); return GRIB_ENCODING_ERROR; } } return GRIB_SUCCESS; }*/ static int check_geometry(grib_handle* handle, const grib_util_grid_spec* spec, size_t data_values_count, bool specified_as_global) { int err = 0; if (spec->pl && spec->pl_size != 0 && (spec->grid_type == GRIB_UTIL_GRID_SPEC_REDUCED_GG || spec->grid_type == GRIB_UTIL_GRID_SPEC_REDUCED_ROTATED_GG)) { if (specified_as_global) { char msg[100] = {0,}; size_t sum = 0; strcpy(msg, "Specified to be global (in spec)"); sum = sum_of_pl_array(spec->pl, spec->pl_size); if (sum != data_values_count) { fprintf(stderr, "%s: Invalid reduced gaussian grid: %s but data_values_count != sum_of_pl_array (%zu!=%zu)\n", __func__, msg, data_values_count, sum); return GRIB_WRONG_GRID; } } } return err; } #if defined(CHECK_HANDLE_AGAINST_SPEC) /* Check what is coded in the handle is what is requested by the spec. */ /* Return GRIB_SUCCESS if the geometry matches, otherwise the error code */ static int check_handle_against_spec(const grib_handle* handle, const long edition, const grib_util_grid_spec* spec, int global_grid) { int err = 0; int check_latitudes = 1; int check_longitudes = 1; long angleSubdivisions = 0; double angular_precision = 1.0/1000.0; /* millidegree by default */ double tolerance = 0; if (edition == 2) { return GRIB_SUCCESS; /* For now only do checks on edition 1 */ } if ((err = grib_get_long(handle, "angleSubdivisions", &angleSubdivisions))==GRIB_SUCCESS) { angular_precision = 1.0/angleSubdivisions; } tolerance = angular_precision/2.0; if (spec->grid_type == GRIB_UTIL_GRID_SPEC_POLAR_STEREOGRAPHIC || spec->grid_type == GRIB_UTIL_GRID_SPEC_SH) { return GRIB_SUCCESS; } /* Cannot check latitudes of Gaussian grids because are always sub-millidegree */ /* and for GRIB1 will differ from the encoded values. We accept this discrepancy! */ if (spec->grid_type == GRIB_UTIL_GRID_SPEC_REGULAR_GG || spec->grid_type == GRIB_UTIL_GRID_SPEC_ROTATED_GG || spec->grid_type == GRIB_UTIL_GRID_SPEC_REDUCED_GG || spec->grid_type == GRIB_UTIL_GRID_SPEC_REDUCED_ROTATED_GG) { if (edition == 1) { check_latitudes = 0; } } /* GRIB-922 */ /* Specification was to make the resulting grid global so no point checking for */ /* input lat/lon values as they would be reset by setting the "global" key to 1 */ if (global_grid) { check_latitudes = check_longitudes = 0; } if (check_latitudes) { double lat1, lat2; const double lat1spec = normalise_angle(spec->latitudeOfFirstGridPointInDegrees); const double lat2spec = normalise_angle(spec->latitudeOfLastGridPointInDegrees); if ((err = grib_get_double(handle, "latitudeOfFirstGridPointInDegrees", &lat1))!=0) return err; if ((err = grib_get_double(handle, "latitudeOfLastGridPointInDegrees", &lat2))!=0) return err; lat1 = normalise_angle(lat1); lat2 = normalise_angle(lat2); if (angle_too_small(lat1spec, angular_precision)) { fprintf(stderr, "Failed to encode latitude of first grid point %.10e: less than angular precision\n",lat1spec); return GRIB_WRONG_GRID; } if (angle_too_small(lat2spec, angular_precision)) { fprintf(stderr, "Failed to encode latitude of last grid point %.10e: less than angular precision\n", lat2spec); return GRIB_WRONG_GRID; } if (!DBL_EQUAL(lat1spec, lat1, tolerance)) { fprintf(stderr, "Failed to encode latitude of first grid point: spec=%.10e val=%.10e\n", lat1spec, lat1); return GRIB_WRONG_GRID; } if (!DBL_EQUAL(lat2spec, lat2, tolerance)) { fprintf(stderr, "Failed to encode latitude of last grid point: spec=%.10e val=%.10e\n", lat2spec, lat2); return GRIB_WRONG_GRID; } } if (check_longitudes) { double lon1, lon2; const double lon1spec = normalise_angle(spec->longitudeOfFirstGridPointInDegrees); const double lon2spec = normalise_angle(spec->longitudeOfLastGridPointInDegrees); if ((err = grib_get_double(handle, "longitudeOfFirstGridPointInDegrees", &lon1))!=0) return err; if ((err = grib_get_double(handle, "longitudeOfLastGridPointInDegrees", &lon2))!=0) return err; lon1 = normalise_angle(lon1); lon2 = normalise_angle(lon2); if (angle_too_small(lon1spec, angular_precision)) { fprintf(stderr, "Failed to encode longitude of first grid point %.10e: less than angular precision\n", lon1spec); return GRIB_WRONG_GRID; } if (angle_too_small(lon2spec, angular_precision)) { fprintf(stderr, "Failed to encode longitude of last grid point %.10e: less than angular precision\n", lon2spec); return GRIB_WRONG_GRID; } if (!DBL_EQUAL(lon1spec, lon1, tolerance)) { fprintf(stderr, "Failed to encode longitude of first grid point: spec=%.10e val=%.10e\n", lon1spec, lon1); return GRIB_WRONG_GRID; } if (!DBL_EQUAL(lon2spec, lon2, tolerance)){ fprintf(stderr, "Failed to encode longitude of last grid point: spec=%.10e val=%.10e\n", lon2spec, lon2); return GRIB_WRONG_GRID; } } if (spec->grid_type == GRIB_UTIL_GRID_SPEC_ROTATED_LL || spec->grid_type == GRIB_UTIL_GRID_SPEC_ROTATED_GG || spec->grid_type == GRIB_UTIL_GRID_SPEC_REDUCED_ROTATED_GG) { double latp, lonp; const double latspec = normalise_angle(spec->latitudeOfSouthernPoleInDegrees); const double lonspec = normalise_angle(spec->longitudeOfSouthernPoleInDegrees); if ((err = grib_get_double(handle, "latitudeOfSouthernPoleInDegrees", &latp))!=0) return err; if ((err = grib_get_double(handle, "longitudeOfSouthernPoleInDegrees", &lonp))!=0) return err; latp = normalise_angle(latp); lonp = normalise_angle(lonp); if (!DBL_EQUAL(latspec, latp, tolerance)) { fprintf(stderr, "Failed to encode latitude of southern pole: spec=%.10e val=%.10e\n",latspec,latp); return GRIB_WRONG_GRID; } if (!DBL_EQUAL(lonspec, lonp, tolerance)) { fprintf(stderr, "Failed to encode longitude of southern pole: spec=%.10e val=%.10e\n",lonspec,lonp); return GRIB_WRONG_GRID; } } return GRIB_SUCCESS; } #endif static bool grid_type_is_supported_in_edition(const int spec_grid_type, const long edition) { if (edition == 1) { if (spec_grid_type == GRIB_UTIL_GRID_SPEC_UNSTRUCTURED || spec_grid_type == GRIB_UTIL_GRID_SPEC_HEALPIX || spec_grid_type == GRIB_UTIL_GRID_SPEC_LAMBERT_AZIMUTHAL_EQUAL_AREA) { return false; } } return true; } static const char* get_grid_type_name(const int spec_grid_type) { if (spec_grid_type == GRIB_UTIL_GRID_SPEC_REGULAR_LL) return "regular_ll"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_ROTATED_LL) return "rotated_ll"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_REGULAR_GG) return "regular_gg"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_ROTATED_GG) return "rotated_gg"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_REDUCED_LL) return "reduced_ll"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_POLAR_STEREOGRAPHIC) return "polar_stereographic"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_REDUCED_GG) return "reduced_gg"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_SH) return "sh"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_REDUCED_ROTATED_GG) return "reduced_rotated_gg"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_LAMBERT_AZIMUTHAL_EQUAL_AREA) return "lambert_azimuthal_equal_area"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_LAMBERT_CONFORMAL) return "lambert"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_HEALPIX) return "healpix"; if (spec_grid_type == GRIB_UTIL_GRID_SPEC_UNSTRUCTURED) return "unstructured_grid"; return NULL; } static bool is_constant_field(const double missingValue, const double* data_values, size_t data_values_count) { size_t ii = 0; bool constant = true; double value = missingValue; for (ii = 0; ii < data_values_count; ii++) { if (data_values[ii] != missingValue) { if (value == missingValue) { value = data_values[ii]; } else { if (value != data_values[ii]) { constant = false; break; } } } } return constant; } // Utility function for when we fail to set the GRIB data values. // Write out a text file called error.data containing the count of values // and the actual values as doubles static int write_out_error_data_file(const double* data_values, size_t data_values_count) { FILE* ferror = fopen("error.data", "w"); size_t lcount = 0; fprintf(ferror, "# data_values_count=%zu\n", data_values_count); fprintf(ferror, "set values={ "); for (size_t ii = 0; ii < data_values_count - 1; ii++) { fprintf(ferror, "%g, ", data_values[ii]); if (lcount > 10) { fprintf(ferror, "\n"); lcount = 0; } lcount++; } fprintf(ferror, "%g }", data_values[data_values_count - 1]); fclose(ferror); return GRIB_SUCCESS; } static long get_bitsPerValue_for_packingType(const int specPackingType, const long specBitsPerValue) { if (specPackingType == GRIB_UTIL_PACKING_TYPE_GRID_SIMPLE) { if (specBitsPerValue > 60) return 60; } else if (specPackingType == GRIB_UTIL_PACKING_TYPE_GRID_SECOND_ORDER) { if (specBitsPerValue > 60) return 32; } else if (specPackingType == GRIB_UTIL_PACKING_TYPE_CCSDS) { if (specBitsPerValue > 32) return 32; } return specBitsPerValue; //original } static int get_grib_sample_name(grib_handle* h, long editionNumber, const grib_util_grid_spec* spec, const char* grid_type, char* sample_name) { const size_t sample_name_len = 1024; switch (spec->grid_type) { case GRIB_UTIL_GRID_SPEC_REDUCED_GG: case GRIB_UTIL_GRID_SPEC_REDUCED_ROTATED_GG: // Choose a sample with the right Gaussian number and edition snprintf(sample_name, sample_name_len, "%s_pl_%ld_grib%ld", grid_type, spec->N, editionNumber); if (spec->pl && spec->pl_size) { // GRIB-834: pl is given so can use any of the reduced_gg_pl samples snprintf(sample_name, sample_name_len, "%s_pl_grib%ld", grid_type, editionNumber); } break; case GRIB_UTIL_GRID_SPEC_LAMBERT_AZIMUTHAL_EQUAL_AREA: case GRIB_UTIL_GRID_SPEC_UNSTRUCTURED: case GRIB_UTIL_GRID_SPEC_LAMBERT_CONFORMAL: case GRIB_UTIL_GRID_SPEC_HEALPIX: snprintf(sample_name, sample_name_len, "GRIB%ld", editionNumber); break; case GRIB_UTIL_GRID_SPEC_REDUCED_LL: snprintf(sample_name, sample_name_len, "%s_sfc_grib%ld", grid_type, editionNumber); break; default: snprintf(sample_name, sample_name_len, "%s_pl_grib%ld", grid_type, editionNumber); } if (spec->pl && spec->grid_name) { // Cannot have BOTH pl and grid name specified fprintf(stderr, "%s: Cannot set BOTH spec.pl and spec.grid_name!\n", __func__); return GRIB_INTERNAL_ERROR; } if (spec->grid_name) { snprintf(sample_name, sample_name_len, "%s_grib%ld", spec->grid_name, editionNumber); } return GRIB_SUCCESS; } grib_handle* grib_util_set_spec(grib_handle* h, const grib_util_grid_spec* spec, const grib_util_packing_spec* packing_spec, int flags, const double* data_values, size_t data_values_count, int* err) { #define SET_LONG_VALUE(n, v) \ do { \ ECCODES_ASSERT(count < 1024); \ values[count].name = n; \ values[count].type = GRIB_TYPE_LONG; \ values[count].long_value = v; \ count++; \ } while (0) #define SET_DOUBLE_VALUE(n, v) \ do { \ ECCODES_ASSERT(count < 1024); \ values[count].name = n; \ values[count].type = GRIB_TYPE_DOUBLE; \ values[count].double_value = v; \ count++; \ } while (0) #define SET_STRING_VALUE(n, v) \ do { \ ECCODES_ASSERT(count < 1024); \ values[count].name = n; \ values[count].type = GRIB_TYPE_STRING; \ values[count].string_value = v; \ count++; \ } while (0) #define COPY_SPEC_LONG(x) \ do { \ ECCODES_ASSERT(count < 1024); \ values[count].name = #x; \ values[count].type = GRIB_TYPE_LONG; \ values[count].long_value = spec->x; \ count++; \ } while (0) #define COPY_SPEC_DOUBLE(x) \ do { \ ECCODES_ASSERT(count < 1024); \ values[count].name = #x; \ values[count].type = GRIB_TYPE_DOUBLE; \ values[count].double_value = spec->x; \ count++; \ } while (0) grib_values values[1024] = {{0,},}; const grib_context* c = grib_context_get_default(); grib_handle* h_out = NULL; grib_handle* h_sample = NULL; const char* grid_type = NULL; char sample_name[1024]; // name of the GRIB sample file char input_grid_type[100]; char input_packing_type[100]; long editionNumber = 0; size_t count = 0, len = 100, slen = 20, input_grid_type_len = 100; double laplacianOperator; int i = 0, packingTypeIsSet = 0, setSecondOrder = 0, setJpegPacking = 0, setCcsdsPacking = 0; bool convertEditionEarlier = false; // For cases when we cannot set some keys without converting bool grib1_high_resolution_fix = false; // See GRIB-863 bool global_grid = false; int expandBoundingBox = 0; ECCODES_ASSERT(h); // Get edition number from input handle if ((*err = grib_get_long(h, "edition", &editionNumber)) != 0) { if (c->write_on_fail) grib_write_message(h, "error.grib", "w"); return NULL; } if (packing_spec->deleteLocalDefinition) { SET_LONG_VALUE("deleteLocalDefinition", 1); } grib_get_string(h, "packingType", input_packing_type, &len); // ECC-1201, ECC-1529, ECC-1530: Make sure input packing type is preserved if (packing_spec->packing == GRIB_UTIL_PACKING_SAME_AS_INPUT && packing_spec->packing_type == GRIB_UTIL_PACKING_TYPE_SAME_AS_INPUT) { if (STR_EQUAL(input_packing_type, "grid_ieee")) { SET_STRING_VALUE("packingType", input_packing_type); } if (STR_EQUAL(input_packing_type, "grid_ccsds")) { setCcsdsPacking = 1; } if (STR_EQUAL(input_packing_type, "grid_second_order")) { setSecondOrder = 1; } } /*if ( (*err=check_values(data_values, data_values_count))!=GRIB_SUCCESS ) { fprintf(stderr,"GRIB_UTIL_SET_SPEC: Data values check failed! %s\n", grib_get_error_message(*err)); goto cleanup; }*/ /* ECC-1269: * Code that was here was moved to "deprecated" directory * See grib_util.GRIB_UTIL_SET_SPEC_FLAGS_ONLY_PACKING. * Dealing with obsolete option GRIB_UTIL_SET_SPEC_FLAGS_ONLY_PACKING */ grid_type = get_grid_type_name(spec->grid_type); if (!grid_type) { fprintf(stderr, "%s: Unknown spec.grid_type (%d)\n", __func__, spec->grid_type); *err = GRIB_NOT_IMPLEMENTED; return NULL; } SET_STRING_VALUE("gridType", grid_type); // The "pl" is given from the template, but "section_copy" will take care of setting the right headers if (get_grib_sample_name(h, editionNumber, spec, grid_type, sample_name) != GRIB_SUCCESS) { goto cleanup; } if (!grid_type_is_supported_in_edition(spec->grid_type, editionNumber)) { fprintf(stderr, "ECCODES WARNING %s: '%s' specified " "but input is GRIB edition %ld. Output must be a higher edition!\n", __func__, grid_type, editionNumber); convertEditionEarlier = true; } h_sample = grib_handle_new_from_samples(NULL, sample_name); if (!h_sample) { *err = GRIB_INVALID_FILE; return NULL; } // Set grid switch (spec->grid_type) { case GRIB_UTIL_GRID_SPEC_REGULAR_LL: case GRIB_UTIL_GRID_SPEC_ROTATED_LL: COPY_SPEC_LONG(bitmapPresent); if (spec->missingValue) COPY_SPEC_DOUBLE(missingValue); SET_LONG_VALUE("ijDirectionIncrementGiven", 1); if (editionNumber == 1) { // GRIB-863: GRIB1 cannot represent increments less than a millidegree if (!angle_can_be_encoded(h, spec->iDirectionIncrementInDegrees) || !angle_can_be_encoded(h, spec->jDirectionIncrementInDegrees)) { grib1_high_resolution_fix = true; // Set flag to compute the increments SET_LONG_VALUE("ijDirectionIncrementGiven", 0); } } // default iScansNegatively=0 jScansPositively=0 is ok COPY_SPEC_LONG(iScansNegatively); COPY_SPEC_LONG(jScansPositively); COPY_SPEC_LONG(Ni); COPY_SPEC_LONG(Nj); COPY_SPEC_DOUBLE(iDirectionIncrementInDegrees); COPY_SPEC_DOUBLE(jDirectionIncrementInDegrees); COPY_SPEC_DOUBLE(longitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(longitudeOfLastGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfLastGridPointInDegrees); break; case GRIB_UTIL_GRID_SPEC_REGULAR_GG: case GRIB_UTIL_GRID_SPEC_ROTATED_GG: COPY_SPEC_LONG(bitmapPresent); if (spec->missingValue) COPY_SPEC_DOUBLE(missingValue); SET_LONG_VALUE("ijDirectionIncrementGiven", 1); // TODO(masn): add ECCODES_ASSERT COPY_SPEC_LONG(Ni); COPY_SPEC_DOUBLE(iDirectionIncrementInDegrees); COPY_SPEC_LONG(Nj); COPY_SPEC_LONG(N); COPY_SPEC_DOUBLE(longitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(longitudeOfLastGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfLastGridPointInDegrees); break; case GRIB_UTIL_GRID_SPEC_REDUCED_LL: COPY_SPEC_LONG(bitmapPresent); if (spec->missingValue) COPY_SPEC_DOUBLE(missingValue); SET_LONG_VALUE("ijDirectionIncrementGiven", 0); COPY_SPEC_LONG(Nj); COPY_SPEC_DOUBLE(longitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(longitudeOfLastGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfLastGridPointInDegrees); break; case GRIB_UTIL_GRID_SPEC_POLAR_STEREOGRAPHIC: COPY_SPEC_LONG(bitmapPresent); if (spec->missingValue) COPY_SPEC_DOUBLE(missingValue); COPY_SPEC_DOUBLE(longitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfFirstGridPointInDegrees); COPY_SPEC_LONG(Ni); COPY_SPEC_LONG(Nj); // default iScansNegatively=0 jScansPositively=0 is ok COPY_SPEC_LONG(iScansNegatively); COPY_SPEC_LONG(jScansPositively); COPY_SPEC_DOUBLE(orientationOfTheGridInDegrees); COPY_SPEC_LONG(DxInMetres); COPY_SPEC_LONG(DyInMetres); break; case GRIB_UTIL_GRID_SPEC_LAMBERT_AZIMUTHAL_EQUAL_AREA: COPY_SPEC_LONG(bitmapPresent); if (spec->missingValue) COPY_SPEC_DOUBLE(missingValue); COPY_SPEC_DOUBLE(longitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfFirstGridPointInDegrees); COPY_SPEC_LONG(Ni); // same as Nx COPY_SPEC_LONG(Nj); // same as Ny COPY_SPEC_LONG(iScansNegatively); COPY_SPEC_LONG(jScansPositively); // TODO(masn): pass in extra keys e.g. Dx, Dy, standardParallel and centralLongitude // COPY_SPEC_LONG(DxInMetres); // COPY_SPEC_LONG(DyInMetres); // COPY_SPEC_LONG(xDirectionGridLengthInMillimetres); // COPY_SPEC_LONG(yDirectionGridLengthInMillimetres); // COPY_SPEC_LONG(standardParallelInMicrodegrees); // COPY_SPEC_LONG(centralLongitudeInMicrodegrees); break; case GRIB_UTIL_GRID_SPEC_UNSTRUCTURED: COPY_SPEC_LONG(bitmapPresent); if (spec->missingValue) COPY_SPEC_DOUBLE(missingValue); // TODO(masn): Other keys break; case GRIB_UTIL_GRID_SPEC_LAMBERT_CONFORMAL: COPY_SPEC_LONG(bitmapPresent); if (spec->missingValue) COPY_SPEC_DOUBLE(missingValue); COPY_SPEC_DOUBLE(longitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfFirstGridPointInDegrees); COPY_SPEC_LONG(Ni); // same as Nx COPY_SPEC_LONG(Nj); // same as Ny COPY_SPEC_LONG(iScansNegatively); COPY_SPEC_LONG(jScansPositively); COPY_SPEC_DOUBLE(latitudeOfSouthernPoleInDegrees); COPY_SPEC_DOUBLE(longitudeOfSouthernPoleInDegrees); COPY_SPEC_LONG(uvRelativeToGrid); // Note: DxInMetres and DyInMetres // should be 'double' and not integer. WMO GRIB2 uses millimetres! // TODO(masn): Add other keys like Latin1, LoV etc break; case GRIB_UTIL_GRID_SPEC_HEALPIX: COPY_SPEC_LONG(bitmapPresent); if (spec->missingValue) COPY_SPEC_DOUBLE(missingValue); COPY_SPEC_LONG(N); // Nside COPY_SPEC_DOUBLE(longitudeOfFirstGridPointInDegrees); break; case GRIB_UTIL_GRID_SPEC_REDUCED_GG: case GRIB_UTIL_GRID_SPEC_REDUCED_ROTATED_GG: COPY_SPEC_LONG(bitmapPresent); if (spec->missingValue) COPY_SPEC_DOUBLE(missingValue); SET_LONG_VALUE("ijDirectionIncrementGiven", 0); COPY_SPEC_LONG(Nj); COPY_SPEC_LONG(N); COPY_SPEC_DOUBLE(longitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(longitudeOfLastGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfFirstGridPointInDegrees); COPY_SPEC_DOUBLE(latitudeOfLastGridPointInDegrees); break; case GRIB_UTIL_GRID_SPEC_SH: *err = grib_get_string(h, "gridType", input_grid_type, &input_grid_type_len); SET_LONG_VALUE("J", spec->truncation); SET_LONG_VALUE("K", spec->truncation); SET_LONG_VALUE("M", spec->truncation); if (packing_spec->packing_type == GRIB_UTIL_PACKING_TYPE_SPECTRAL_COMPLEX) { const long JS = spec->truncation < 20 ? spec->truncation : 20; SET_STRING_VALUE("packingType", "spectral_complex"); packingTypeIsSet = 1; SET_LONG_VALUE("JS", JS); SET_LONG_VALUE("KS", JS); SET_LONG_VALUE("MS", JS); if (packing_spec->packing == GRIB_UTIL_PACKING_USE_PROVIDED && editionNumber == 2) { SET_LONG_VALUE("computeLaplacianOperator", 1); } else if ((!(*err) && strcmp(input_grid_type, "sh")) || packing_spec->computeLaplacianOperator) { SET_LONG_VALUE("computeLaplacianOperator", 1); if (packing_spec->truncateLaplacian) SET_LONG_VALUE("truncateLaplacian", 1); } else { SET_LONG_VALUE("computeLaplacianOperator", 0); *err = grib_get_double(h, "laplacianOperator", &laplacianOperator); if (packing_spec->truncateLaplacian) SET_LONG_VALUE("truncateLaplacian", 1); SET_DOUBLE_VALUE("laplacianOperator", packing_spec->laplacianOperator); if (laplacianOperator) { SET_DOUBLE_VALUE("laplacianOperator", laplacianOperator); } } } break; } // Set rotation switch (spec->grid_type) { case GRIB_UTIL_GRID_SPEC_ROTATED_LL: case GRIB_UTIL_GRID_SPEC_ROTATED_GG: case GRIB_UTIL_GRID_SPEC_REDUCED_ROTATED_GG: COPY_SPEC_LONG(uvRelativeToGrid); COPY_SPEC_DOUBLE(latitudeOfSouthernPoleInDegrees); COPY_SPEC_DOUBLE(longitudeOfSouthernPoleInDegrees); COPY_SPEC_DOUBLE(angleOfRotationInDegrees); break; } // process packing options if (!packingTypeIsSet && packing_spec->packing == GRIB_UTIL_PACKING_USE_PROVIDED && strcmp(input_packing_type, "grid_simple_matrix")) { switch (packing_spec->packing_type) { case GRIB_UTIL_PACKING_TYPE_SPECTRAL_COMPLEX: if (strcmp(input_packing_type, "spectral_complex") && !strcmp(input_packing_type, "spectral_simple")) SET_STRING_VALUE("packingType", "spectral_complex"); break; case GRIB_UTIL_PACKING_TYPE_SPECTRAL_SIMPLE: if (strcmp(input_packing_type, "spectral_simple") && !strcmp(input_packing_type, "spectral_complex")) SET_STRING_VALUE("packingType", "spectral_simple"); break; case GRIB_UTIL_PACKING_TYPE_GRID_SIMPLE: if (strcmp(input_packing_type, "grid_simple") && !strcmp(input_packing_type, "grid_complex")) SET_STRING_VALUE("packingType", "grid_simple"); break; case GRIB_UTIL_PACKING_TYPE_GRID_COMPLEX: if (!STR_EQUAL(input_packing_type, "grid_complex")) { SET_STRING_VALUE("packingType", "grid_complex"); convertEditionEarlier = true; } break; case GRIB_UTIL_PACKING_TYPE_JPEG: /* Have to delay JPEG packing: * Reason 1: It is not available in GRIB1 and so we have to wait until we change edition * Reason 2: It has to be done AFTER we set the data values */ if (strcmp(input_packing_type, "grid_jpeg")) setJpegPacking = 1; break; case GRIB_UTIL_PACKING_TYPE_CCSDS: /* Have to delay CCSDS packing: * Reason 1: It is not available in GRIB1 and so we have to wait until we change edition * Reason 2: It has to be done AFTER we set the data values */ if (!STR_EQUAL(input_packing_type, "grid_ccsds")) setCcsdsPacking = 1; break; case GRIB_UTIL_PACKING_TYPE_IEEE: if ( !STR_EQUAL(input_packing_type, "grid_ieee") ) SET_STRING_VALUE("packingType", "grid_ieee"); break; case GRIB_UTIL_PACKING_TYPE_GRID_SECOND_ORDER: /* we delay the set of grid_second_order because we don't want to do it on a field with bitsPerValue=0 */ setSecondOrder = 1; break; default: fprintf(stderr, "%s: invalid packing_spec.packing_type (%ld)\n", __func__, packing_spec->packing_type); *err = GRIB_INTERNAL_ERROR; goto cleanup; } } if (strcmp(input_packing_type, "grid_simple_matrix") == 0) { long numberOfDirections, numberOfFrequencies; int keep_matrix = h->context->keep_matrix; if (packing_spec->packing_type == GRIB_UTIL_PACKING_TYPE_GRID_SIMPLE) { keep_matrix = 0; // ECC-911 } if (keep_matrix) { SET_STRING_VALUE("packingType", "grid_simple_matrix"); if (GRIB_SUCCESS == grib_get_long(h, "numberOfDirections", &numberOfDirections)) { grib_get_long(h, "numberOfDirections", &numberOfDirections); SET_LONG_VALUE("NC1", numberOfDirections); grib_get_long(h, "numberOfFrequencies", &numberOfFrequencies); SET_LONG_VALUE("NC2", numberOfFrequencies); SET_LONG_VALUE("physicalFlag1", 1); SET_LONG_VALUE("physicalFlag2", 2); SET_LONG_VALUE("NR", 1); SET_LONG_VALUE("NC", 1); } } else { SET_STRING_VALUE("packingType", "grid_simple"); } } switch (packing_spec->accuracy) { case GRIB_UTIL_ACCURACY_SAME_BITS_PER_VALUES_AS_INPUT: { long bitsPerValue = 0; if ((packing_spec->packing_type == GRIB_UTIL_PACKING_TYPE_GRID_SIMPLE || packing_spec->packing_type == GRIB_UTIL_PACKING_TYPE_CCSDS) && strcmp(input_packing_type, "grid_ieee")==0) { SET_LONG_VALUE("bitsPerValue", 32); } else { ECCODES_ASSERT(grib_get_long(h, "bitsPerValue", &bitsPerValue) == 0); SET_LONG_VALUE("bitsPerValue", bitsPerValue); } } break; case GRIB_UTIL_ACCURACY_USE_PROVIDED_BITS_PER_VALUES: { // See ECC-1921 const long bitsPerValue = get_bitsPerValue_for_packingType(packing_spec->packing_type, packing_spec->bitsPerValue); if (bitsPerValue != packing_spec->bitsPerValue) { fprintf(stderr, "ECCODES WARNING : Cannot pack with requested bitsPerValue (%ld). Using %ld\n", packing_spec->bitsPerValue, bitsPerValue); } SET_LONG_VALUE("bitsPerValue", bitsPerValue); } break; case GRIB_UTIL_ACCURACY_SAME_DECIMAL_SCALE_FACTOR_AS_INPUT: { long decimalScaleFactor = 0; ECCODES_ASSERT(grib_get_long(h, "decimalScaleFactor", &decimalScaleFactor) == 0); SET_LONG_VALUE("decimalScaleFactor", decimalScaleFactor); } break; case GRIB_UTIL_ACCURACY_USE_PROVIDED_DECIMAL_SCALE_FACTOR: SET_LONG_VALUE("decimalScaleFactor", packing_spec->decimalScaleFactor); break; default: fprintf(stderr, "%s: invalid packing_spec.accuracy (%ld)\n", __func__, packing_spec->accuracy); grib_handle_delete(h_sample); *err = GRIB_INTERNAL_ERROR; goto cleanup; } if (packing_spec->extra_settings_count) { for (i = 0; i < packing_spec->extra_settings_count; i++) { ECCODES_ASSERT(count < 1024); if (strcmp(packing_spec->extra_settings[i].name, "expandBoundingBox") == 0) { if (packing_spec->extra_settings[i].long_value == 1) { /* ECC-625: Request is for expansion of bounding box (sub-area). * This is also called the "snap-out" policy */ expandBoundingBox = 1; } } else { values[count++] = packing_spec->extra_settings[i]; if (strcmp(packing_spec->extra_settings[i].name, "global") == 0 && packing_spec->extra_settings[i].long_value == 1) { /* GRIB-922: Request is for a global grid. Setting this key will * calculate the lat/lon values. So the spec's lat/lon can be ignored */ global_grid = true; } } } } // grib_write_message(h,"input.grib","w"); // grib_write_message(h_sample,"geo.grib","w"); // copy product and local sections from h to h_sample handle and store in h_out if ((h_out = grib_util_sections_copy(h, h_sample, GRIB_SECTION_PRODUCT | GRIB_SECTION_LOCAL, err)) == NULL) { goto cleanup; } grib_handle_delete(h_sample); ECCODES_ASSERT(*err == 0); // GRIB-857: Set "pl" array if provided (For reduced Gaussian grids) ECCODES_ASSERT(spec->pl_size >= 0); if (spec->pl && spec->pl_size == 0) { fprintf(stderr, "%s: pl array not NULL but pl_size == 0!\n", __func__); goto cleanup; } if (spec->pl_size > 0 && spec->pl == NULL) { fprintf(stderr, "%s: pl_size not zero but pl array == NULL!\n", __func__); goto cleanup; } if (spec->pl_size != 0 && (spec->grid_type == GRIB_UTIL_GRID_SPEC_REDUCED_GG || spec->grid_type == GRIB_UTIL_GRID_SPEC_REDUCED_ROTATED_GG)) { *err = grib_set_long_array(h_out, "pl", spec->pl, spec->pl_size); if (*err) { fprintf(stderr, "%s: Cannot set pl: %s\n", __func__, grib_get_error_message(*err)); goto cleanup; } if (global_grid) { size_t sum = sum_of_pl_array(spec->pl, spec->pl_size); if (data_values_count != sum) { fprintf(stderr, "%s: invalid reduced gaussian grid: " "specified as global, data_values_count=%zu but sum of pl array=%zu\n", __func__, data_values_count, sum); *err = GRIB_WRONG_GRID; goto cleanup; } } } if (h->context->debug == -1) { fprintf(stderr, "ECCODES DEBUG grib_util: global_grid = %d\n", global_grid); fprintf(stderr, "ECCODES DEBUG grib_util: expandBoundingBox = %d\n", expandBoundingBox); print_values(h->context, spec, packing_spec, input_packing_type, data_values, data_values_count, values, count); } // Apply adjustments to bounding box if needed if (expandBoundingBox) { if ((*err = expand_bounding_box(h_out, values, count)) != 0) { fprintf(stderr, "%s: Cannot expand bounding box: %s\n", __func__, grib_get_error_message(*err)); if (h->context->write_on_fail) grib_write_message(h_out, "error.grib", "w"); goto cleanup; } } if (convertEditionEarlier && packing_spec->editionNumber > 1) { // Note: // If the input is GRIB1 and the requested grid type is HealPix or ORCA etc, // we deliberately fail unless the user specifies edition conversion. // i.e., we do not automatically convert edition // If we later change our mind, we need to change editionNumber to 2 here: // long new_edition = packing_spec->editionNumber; // if (new_edition == 0) new_edition = 2; // *err = grib_set_long(h_out, "edition", packing_spec->editionNumber); if (*err) { fprintf(stderr, "%s: Cannot convert to edition %ld.\n", __func__, packing_spec->editionNumber); goto cleanup; } } if ((*err = grib_set_values(h_out, values, count)) != 0) { fprintf(stderr, "%s: Cannot set key values: %s\n", __func__, grib_get_error_message(*err)); for (i = 0; i < count; i++) if (values[i].error) fprintf(stderr, " %s %s\n", values[i].name, grib_get_error_message(values[i].error)); goto cleanup; } if ((*err = grib_set_double_array(h_out, "values", data_values, data_values_count)) != GRIB_SUCCESS) { write_out_error_data_file(data_values, data_values_count); if (c->write_on_fail) grib_write_message(h_out, "error.grib", "w"); goto cleanup; } /* grib_write_message(h_out,"h.grib","w"); */ /* if the field is empty GRIBEX is packing as simple*/ /* if (!strcmp(input_packing_type,"grid_simple_matrix")) { long numberOfValues; grib_get_long(h_out,"numberOfValues",&numberOfValues); if (numberOfValues==0) { slen=11; grib_set_string(h_out,"packingType","grid_simple",&slen); } } */ if (grib1_high_resolution_fix) { // GRIB-863: must set increments to MISSING // increments are not coded in message but computed if ((*err = grib_set_missing(h_out, "iDirectionIncrement")) != 0) { fprintf(stderr, "%s: Cannot set Di to missing: %s\n", __func__, grib_get_error_message(*err)); goto cleanup; } if ((*err = grib_set_missing(h_out, "jDirectionIncrement")) != 0) { fprintf(stderr, "%s: Cannot set Dj to missing: %s\n", __func__, grib_get_error_message(*err)); goto cleanup; } } //grib_dump_content(h_out, stdout,"debug", ~0, NULL); // convert to second_order if not constant field. (Also see ECC-326) if (setSecondOrder) { double missingValue = 0; grib_get_double(h_out, "missingValue", &missingValue); bool constant = is_constant_field(missingValue, data_values, data_values_count); if (!constant) { if (editionNumber == 1) { long numberOfGroups = 0; grib_handle* htmp = grib_handle_clone(h_out); slen = 17; grib_set_string(htmp, "packingType", "grid_second_order", &slen); grib_get_long(htmp, "numberOfGroups", &numberOfGroups); // GRIBEX is not able to decode overflown numberOfGroups with SPD if (numberOfGroups > 65534 && h_out->context->no_spd) { slen = 24; grib_set_string(h_out, "packingType", "grid_second_order_no_SPD", &slen); grib_handle_delete(htmp); } else { grib_handle_delete(h_out); h_out = htmp; } } else { slen = 17; grib_set_string(h_out, "packingType", "grid_second_order", &slen); *err = grib_set_double_array(h_out, "values", data_values, data_values_count); if (*err != GRIB_SUCCESS) { fprintf(stderr, "%s: setting data values failed: %s\n", __func__, grib_get_error_message(*err)); goto cleanup; } } } else { if (h_out->context->gribex_mode_on) { h_out->context->gribex_mode_on = 0; grib_set_double_array(h_out, "values", data_values, data_values_count); h_out->context->gribex_mode_on = 1; } } } if (packing_spec->editionNumber && packing_spec->editionNumber != editionNumber) { *err = grib_set_long(h_out, "edition", packing_spec->editionNumber); if (*err != GRIB_SUCCESS) { fprintf(stderr, "%s: Failed to change edition to %ld: %s\n", __func__, packing_spec->editionNumber, grib_get_error_message(*err)); if (h->context->write_on_fail) grib_write_message(h_out, "error.grib", "w"); goto cleanup; } } if (editionNumber > 1 || packing_spec->editionNumber > 1) { // ECC-353 // Some packing types are not available in GRIB1 and have to be done AFTER we set data values if (setJpegPacking == 1) { *err = grib_set_string(h_out, "packingType", "grid_jpeg", &slen); if (*err != GRIB_SUCCESS) { fprintf(stderr, "%s: Failed to change packingType to JPEG: %s\n", __func__, grib_get_error_message(*err)); goto cleanup; } } if (setCcsdsPacking == 1) { *err = grib_set_string(h_out, "packingType", "grid_ccsds", &slen); if (*err != GRIB_SUCCESS) { fprintf(stderr, "%s: Failed to change packingType to CCSDS: %s\n", __func__, grib_get_error_message(*err)); goto cleanup; } } } if (packing_spec->deleteLocalDefinition) { grib_set_long(h_out, "deleteLocalDefinition", 1); } // ECC-445 if (expandBoundingBox) { ECCODES_ASSERT(!global_grid); // ECC-576: "global" should not be set } if ((*err = check_geometry(h_out, spec, data_values_count, global_grid)) != GRIB_SUCCESS) { fprintf(stderr, "%s: Geometry check failed: %s\n", __func__, grib_get_error_message(*err)); if (h->context->write_on_fail) grib_write_message(h_out, "error.grib", "w"); goto cleanup; } /* Disable check: need to re-examine GRIB-864 */ // if ( (*err = check_handle_against_spec(h_out, editionNumber, spec, global_grid)) != GRIB_SUCCESS) { // fprintf(stderr,"GRIB_UTIL_SET_SPEC: Geometry check failed: %s\n", grib_get_error_message(*err)); // if (editionNumber == 1) { // fprintf(stderr,"Note: in GRIB edition 1 latitude and longitude values cannot be represented with sub-millidegree precision.\n"); // } // if (c->write_on_fail) grib_write_message(h_out,"error.grib","w"); // goto cleanup; // } if (h->context->debug == -1) fprintf(stderr, "ECCODES DEBUG grib_util: %s end\n",__func__); return h_out; cleanup: grib_handle_delete(h_out); return NULL; } // int grib_moments(grib_handle* h, double east, double north, double west, double south, int order, double* moments, long* count) // { // grib_iterator* iter = NULL; // int ret = 0, i, j, l; // size_t n = 0, numberOfPoints = 0; // double *lat, *lon, *values; // double vlat, vlon, val; // double dx, dy, ddx, ddy; // double mass, centroidX, centroidY; // double missingValue; // grib_context* c = grib_context_get_default(); // ret = grib_get_size(h, "values", &n); // if (ret) // return ret; // lat = (double*)grib_context_malloc_clear(c, sizeof(double) * n); // lon = (double*)grib_context_malloc_clear(c, sizeof(double) * n); // values = (double*)grib_context_malloc_clear(c, sizeof(double) * n); // iter = grib_iterator_new(h, 0, &ret); // numberOfPoints = 0; // while (grib_iterator_next(iter, &vlat, &vlon, &val)) { // if (vlon >= east && vlon <= west && vlat >= south && vlat <= north) { // lat[numberOfPoints] = vlat; // lon[numberOfPoints] = vlon; // values[numberOfPoints] = val; // numberOfPoints++; // } // } // grib_iterator_delete(iter); // ret = grib_get_double(h, "missingValue", &missingValue); // centroidX = 0; // centroidY = 0; // mass = 0; // *count = 0; // for (i = 0; i < numberOfPoints; i++) { // if (values[i] != missingValue) { // centroidX += lon[i] * values[i]; // centroidY += lat[i] * values[i]; // mass += values[i]; // (*count)++; // } // } // centroidX /= mass; // centroidY /= mass; // mass /= *count; // for (j = 0; j < order * order; j++) // moments[j] = 0; // for (i = 0; i < numberOfPoints; i++) { // if (values[i] != missingValue) { // dx = (lon[i] - centroidX); // dy = (lat[i] - centroidY); // ddx = 1; // for (j = 0; j < order; j++) { // ddy = 1; // for (l = 0; l < order; l++) { // moments[j * order + l] += ddx * ddy * values[i]; // ddy *= dy; // } // ddx *= dx; // } // } // } // for (j = 0; j < order; j++) { // for (l = 0; l < order; l++) { // if (j + l > 1) { // moments[j * order + l] = pow(fabs(moments[j * order + l]), 1.0 / (j + l)) / *count; // } // else { // moments[j * order + l] /= *count; // } // } // } // grib_context_free(c, lat); // grib_context_free(c, lon); // grib_context_free(c, values); // (void)mass; // return ret; // } // Helper function for 'parse_keyval_string' static void set_value(grib_values* value, char* str, int equal) { char *p = 0, *q = 0, *s = 0; char buf[1000] = {0,}; const grib_context* c = grib_context_get_default(); value->equal = equal; q = str; while (*q != '/' && *q != 0) q++; if (*q == '/') { s = grib_context_strdup(c, q + 1); value->next = (grib_values*)grib_context_malloc_clear(c, sizeof(grib_values)); value->next->type = value->type; value->next->name = grib_context_strdup(c, value->name); set_value(value->next, s, equal); grib_context_free(c, s); } memcpy(buf, str, q - str); switch (value->type) { case GRIB_TYPE_DOUBLE: value->double_value = strtod(buf, &p); if (*p != 0) value->has_value = 1; else if (!strcmp(str, "missing") || !strcmp(str, "MISSING") || !strcmp(str, "Missing")) { value->type = GRIB_TYPE_MISSING; value->has_value = 1; } break; case GRIB_TYPE_LONG: errno = 0; // must clear errno before calling strtol value->long_value = strtol(buf, &p, 10); if (*p != 0) value->has_value = 1; else if (!strcmp(buf, "missing") || !strcmp(buf, "MISSING") || !strcmp(buf, "Missing")) { value->type = GRIB_TYPE_MISSING; value->has_value = 1; } break; case GRIB_TYPE_STRING: if (!strcmp(buf, "missing") || !strcmp(buf, "MISSING") || !strcmp(buf, "Missing")) { value->type = GRIB_TYPE_MISSING; value->has_value = 1; } else { value->string_value = grib_context_strdup(c, buf); value->has_value = 1; } break; case GRIB_TYPE_UNDEFINED: errno = 0; // must clear errno before calling strtol value->long_value = strtol(buf, &p, 10); if (*p == 0) { // check the conversion from string to long if (errno == ERANGE && (value->long_value == LONG_MAX || value->long_value == LONG_MIN)) { fprintf(stderr, "ECCODES WARNING : Setting %s=%s causes overflow/underflow\n", value->name, buf); fprintf(stderr, "ECCODES WARNING : Value adjusted to %ld\n", value->long_value); //perror("strtol"); } value->type = GRIB_TYPE_LONG; value->has_value = 1; } else { value->double_value = strtod(buf, &p); if (*p == 0) { value->type = GRIB_TYPE_DOUBLE; value->has_value = 1; } else if (!strcmp(buf, "missing") || !strcmp(buf, "MISSING") || !strcmp(buf, "Missing")) { value->type = GRIB_TYPE_MISSING; value->has_value = 1; } else { value->string_value = grib_context_strdup(c, buf); value->type = GRIB_TYPE_STRING; value->has_value = 1; } } break; } } // // 'grib_tool' Optional tool name which is printed on error. Can be NULL // 'arg' The string to be parsed e.g. key1=value1,key2!=value2 etc (cannot be const) // 'values_required' If true then each key must have a value after it // 'default_type' The default type e.g. GRIB_TYPE_UNDEFINED or GRIB_TYPE_DOUBLE // 'values' The array we populate and return (output) // 'count' Number of elements (output). Must be initialised to the size of the values array // int parse_keyval_string(const char* grib_tool, char* arg, int values_required, int default_type, grib_values values[], int* count) { char* p = NULL; char* lasts = NULL; int i = 0; if (arg == NULL) { *count = 0; return GRIB_SUCCESS; } /* Note: strtok modifies its input argument 'arg' * so it cannot be 'const' */ p = strtok_r(arg, ",", &lasts); while (p != NULL) { values[i].name = (char*)calloc(1, strlen(p) + 1); ECCODES_ASSERT(values[i].name); strcpy((char*)values[i].name, p); p = strtok_r(NULL, ",", &lasts); i++; if (i >= *count) { fprintf(stderr, "Input string contains too many entries (max=%d)\n", *count); return GRIB_ARRAY_TOO_SMALL; } } *count = i; for (i = 0; i < *count; i++) { int equal = 1; char* value = NULL; if (values_required) { // Can be either k=v or k!=v p = (char*)values[i].name; while (*p != '=' && *p != '!' && *p != '\0') p++; if (*p == '=') { *p = '\0'; p++; value = p; equal = 1; } else if (*p == '!' && *(++p) == '=') { *p = '\0'; *(p - 1) = '\0'; p++; value = p; equal = 0; } else { return GRIB_INVALID_ARGUMENT; } } p = (char*)values[i].name; while (*p != ':' && *p != '\0') p++; if (*p == ':') { values[i].type = grib_type_to_int(*(p + 1)); if (*(p + 1) == 'n') values[i].type = CODES_NAMESPACE; *p = '\0'; p++; } else { values[i].type = default_type; } if (values_required) { if (strlen(value) == 0) { if (grib_tool) fprintf(stderr, "%s error: no value provided for key \"%s\"\n", grib_tool, values[i].name); else fprintf(stderr, "Error: no value provided for key \"%s\"\n", values[i].name); return GRIB_INVALID_ARGUMENT; } set_value(&values[i], value, equal); } } return GRIB_SUCCESS; } // Return 1 if the productDefinitionTemplateNumber (GRIB2) is for EPS (ensemble) products int grib2_is_PDTN_EPS(long pdtn) { static int eps_pdtns[] = { 1, 11, 33, 34, 41, 43, 45, 47, 49, 54, 56, 58, 59, 60, 61, 63, 68, 71, 73, 77, 79, 81, 83, 84, 85, 92, 94, 96, 98 }; size_t i = 0, num_epss = (sizeof(eps_pdtns) / sizeof(eps_pdtns[0])); for (i = 0; i < num_epss; ++i) { if (eps_pdtns[i] == pdtn) return 1; } return 0; } // Return 1 if the productDefinitionTemplateNumber (GRIB2) is for plain (vanilla) products int grib2_is_PDTN_Plain(long pdtn) { return ( pdtn == 0 || pdtn == 1 || pdtn == 8 || pdtn == 11); } // Return 1 if the productDefinitionTemplateNumber (GRIB2) is for atmospheric chemical constituents int grib2_is_PDTN_Chemical(long pdtn) { return ( pdtn == 40 || pdtn == 41 || pdtn == 42 || pdtn == 43); } // Return 1 if the productDefinitionTemplateNumber (GRIB2) is for // atmospheric chemical constituents with source or sink int grib2_is_PDTN_ChemicalSourceSink(long pdtn) { return ( pdtn == 76 || pdtn == 77 || pdtn == 78 || pdtn == 79); } // Return 1 if the productDefinitionTemplateNumber (GRIB2) is for // atmospheric chemical constituents based on a distribution function int grib2_is_PDTN_ChemicalDistFunc(long pdtn) { return ( pdtn == 57 || pdtn == 58 || pdtn == 67 || pdtn == 68); } // Return 1 if the productDefinitionTemplateNumber (GRIB2) is for aerosols int grib2_is_PDTN_Aerosol(long pdtn) { // Notes: PDT 44 is deprecated and replaced by 50 // PDT 47 is deprecated and replaced by 85 return ( pdtn == 44 || pdtn == 48 || pdtn == 49 || pdtn == 50 || pdtn == 45 || pdtn == 46 || pdtn == 47 || pdtn == 85); } // Return 1 if the productDefinitionTemplateNumber (GRIB2) is for optical properties of aerosol int grib2_is_PDTN_AerosolOptical(long pdtn) { // Note: PDT 48 can be used for both plain aerosols as well as optical properties of aerosol. // For the former user must set the optical wavelength range to missing return ( pdtn == 48 || pdtn == 49); } // Arguments: // is_det: true for deterministic, false for ensemble // is_instant: true for instantaneous (point-in-time), false for interval-based (statistically processed) int grib2_choose_PDTN(int current_PDTN, bool is_det, bool is_instant) { const bool is_ens = !is_det; const bool is_interval = !is_instant; if (grib2_is_PDTN_Plain(current_PDTN)) { if (is_instant && is_ens) return 1; if (is_instant && is_det) return 0; if (is_interval && is_ens) return 11; if (is_interval && is_det) return 8; } if (grib2_is_PDTN_Chemical(current_PDTN)) { if (is_instant && is_ens) return 41; if (is_instant && is_det) return 40; if (is_interval && is_ens) return 43; if (is_interval && is_det) return 42; } if (grib2_is_PDTN_ChemicalSourceSink(current_PDTN)) { if (is_instant && is_ens) return 77; if (is_instant && is_det) return 76; if (is_interval && is_ens) return 79; if (is_interval && is_det) return 78; } if (grib2_is_PDTN_ChemicalDistFunc(current_PDTN)) { if (is_instant && is_ens) return 58; if (is_instant && is_det) return 57; if (is_interval && is_ens) return 68; if (is_interval && is_det) return 67; } if (current_PDTN == 45 || current_PDTN == 48) { if (is_instant && is_ens) return 45; if (is_instant && is_det) return 48; if (is_interval && is_ens) return 85; if (is_interval && is_det) return 46; } if (current_PDTN == 50) { if (is_instant && is_ens) return 45; } return current_PDTN; // no change } // Given some information about the type of grib2 parameter, return the productDefinitionTemplateNumber to use. // All arguments are booleans (0 or 1) // is_eps: ensemble or deterministic // is_instant: instantaneous or interval-based // etc... int grib2_select_PDTN(int is_eps, int is_instant, int is_chemical, int is_chemical_srcsink, int is_chemical_distfn, int is_aerosol, int is_aerosol_optical) { // At most one has to be set. All could be 0 // Unfortunately if PDTN=48 then both aerosol and aerosol_optical can be 1! const int sum = is_chemical + is_chemical_srcsink + is_chemical_distfn + is_aerosol + is_aerosol_optical; ECCODES_ASSERT(sum == 0 || sum == 1 || sum == 2); if (is_chemical) { if (is_eps) { if (is_instant) return 41; else return 43; } else { if (is_instant) return 40; else return 42; } } if (is_chemical_srcsink) { if (is_eps) { if (is_instant) return 77; else return 79; } else { if (is_instant) return 76; else return 78; } } if (is_chemical_distfn) { if (is_eps) { if (is_instant) return 58; else return 68; } else { if (is_instant) return 57; else return 67; } } if (is_aerosol_optical) { if (is_eps) { if (is_instant) return 49; // WMO does not have a non-instantaneous case here! } else { if (is_instant) return 48; // WMO does not have a non-instantaneous case here! } } if (is_aerosol) { if (is_eps) { if (is_instant) return 45; else return 85; // PDT 47 is deprecated } else { if (is_instant) return 50; // ECC-1963: 44 is deprecated else return 46; } } // Fallthru case: default if (is_eps) { if (is_instant) return 1; else return 11; } else { if (is_instant) return 0; else return 8; } } size_t sum_of_pl_array(const long* pl, size_t plsize) { long i, count = 0; for (i = 0; i < plsize; i++) { count += pl[i]; } return count; } int grib_is_earth_oblate(const grib_handle* h) { long oblate = 0; int err = grib_get_long(h, "earthIsOblate", &oblate); if (!err && oblate == 1) { return 1; } return 0; } int grib_check_data_values_minmax(grib_handle* h, const double min_val, const double max_val) { int result = GRIB_SUCCESS; const grib_context* ctx = h->context; if (!(min_val < DBL_MAX && min_val > -DBL_MAX)) { grib_context_log(ctx, GRIB_LOG_ERROR, "Minimum value out of range: %g", min_val); return GRIB_ENCODING_ERROR; } if (!(max_val < DBL_MAX && max_val > -DBL_MAX)) { grib_context_log(ctx, GRIB_LOG_ERROR, "Maximum value out of range: %g", max_val); return GRIB_ENCODING_ERROR; } // Data Quality checks if (ctx->grib_data_quality_checks) { result = grib_util_grib_data_quality_check(h, min_val, max_val); } return result; } // Return true(1) if large constant fields are to be created, otherwise false(0) int grib_producing_large_constant_fields(const grib_handle* h, int edition) { // First check if the transient key is set const grib_context* c = h->context; long produceLargeConstantFields = 0; if (grib_get_long(h, "produceLargeConstantFields", &produceLargeConstantFields) == GRIB_SUCCESS && produceLargeConstantFields != 0) { return 1; } if (c->gribex_mode_on == 1 && edition == 1) { return 1; } // Finally check the environment variable via the context return c->large_constant_fields; } static std::string grib_data_quality_check_extra_info(const grib_handle* h) { char step[32] = "unknown"; char marsClass[32] = {0,}; char marsStream[32] = {0,}; char marsType[32] = {0,}; std::string result; std::stringstream ss; size_t len = 32; int err1 = grib_get_string(h, "step", step, &len); len = 32; int err2 = grib_get_string(h, "class", marsClass, &len); len = 32; int err3 = grib_get_string(h, "stream", marsStream, &len); len = 32; int err4 = grib_get_string(h, "type", marsType, &len); if (!err1 && !err2 && !err3 && !err4) { ss << "step=" << step << ", class=" << marsClass << ", stream=" << marsStream << ", type=" << marsType; result = ss.str(); } return result; } int grib_util_grib_data_quality_check(grib_handle* h, double min_val, double max_val) { int err = 0; double min_field_value_allowed = 0, max_field_value_allowed = 0; long paramId = 0; const grib_context* ctx = h->context; bool is_error = true; char description[1024] = {0,}; char shortName[64] = {0,}; char name[526] = {0,}; size_t len = 0; const char* invalid_shortName = "unknown"; const char* invalid_name = "Experimental product"; // If grib_data_quality_checks == 1, limits failure results in an error // If grib_data_quality_checks == 2, limits failure results in a warning ECCODES_ASSERT(ctx->grib_data_quality_checks == 1 || ctx->grib_data_quality_checks == 2); is_error = (ctx->grib_data_quality_checks == 1); len = sizeof(shortName); err = grib_get_string(h, "shortName", shortName, &len); if (err || STR_EQUAL(shortName, invalid_shortName)) { std::string info( grib_data_quality_check_extra_info(h) ); fprintf(stderr, "ECCODES %s : (%s) Invalid metadata: shortName='%s'\n", (is_error ? "ERROR" : "WARNING"), info.c_str(), invalid_shortName); if (is_error) return GRIB_INVALID_MESSAGE; } len = sizeof(name); err = grib_get_string(h, "name", name, &len); if (err || STR_EQUAL(name, invalid_name)) { fprintf(stderr, "ECCODES %s : Invalid metadata: name='%s'\n", (is_error ? "ERROR" : "WARNING"), invalid_name); if (is_error) return GRIB_INVALID_MESSAGE; } // The limit keys must exist if we are here err = grib_get_double(h, "param_value_min", &min_field_value_allowed); if (err) { grib_context_log(ctx, GRIB_LOG_ERROR, "grib_data_quality_check: Could not get param_value_min"); return err; } err = grib_get_double(h, "param_value_max", &max_field_value_allowed); if (err) { grib_context_log(ctx, GRIB_LOG_ERROR, "grib_data_quality_check: Could not get param_value_max"); return err; } if (ctx->debug) { if (get_concept_condition_string(h, "param_value_max", NULL, description) == GRIB_SUCCESS) { printf("ECCODES DEBUG grib_data_quality_check: Checking condition '%s' (allowed=%g, %g) (actual=%g, %g)\n", description, min_field_value_allowed, max_field_value_allowed, min_val, max_val); } } if (min_val < min_field_value_allowed) { std::string info( grib_data_quality_check_extra_info(h) ); if (get_concept_condition_string(h, "param_value_min", NULL, description) == GRIB_SUCCESS) { fprintf(stderr, "ECCODES %s : (%s, %s): minimum (%g) is less than the allowable limit (%g)\n", (is_error ? "ERROR" : "WARNING"), description, info.c_str(), min_val, min_field_value_allowed); } else { if (grib_get_long(h, "paramId", ¶mId) == GRIB_SUCCESS) { fprintf(stderr, "ECCODES %s : (paramId=%ld, %s): minimum (%g) is less than the default allowable limit (%g)\n", (is_error ? "ERROR" : "WARNING"), paramId, info.c_str(), min_val, min_field_value_allowed); } } if (is_error) { return GRIB_OUT_OF_RANGE; // Failure } } if (max_val > max_field_value_allowed) { std::string info( grib_data_quality_check_extra_info(h) ); if (get_concept_condition_string(h, "param_value_max", NULL, description) == GRIB_SUCCESS) { fprintf(stderr, "ECCODES %s : (%s, %s): maximum (%g) is more than the allowable limit (%g)\n", (is_error ? "ERROR" : "WARNING"), description, info.c_str(), max_val, max_field_value_allowed); } else { if (grib_get_long(h, "paramId", ¶mId) == GRIB_SUCCESS) { fprintf(stderr, "ECCODES %s : (paramId=%ld, %s): maximum (%g) is more than the default allowable limit (%g)\n", (is_error ? "ERROR" : "WARNING"), paramId, info.c_str(), max_val, max_field_value_allowed); } } if (is_error) { return GRIB_OUT_OF_RANGE; // Failure } } return GRIB_SUCCESS; }