ECC-1733: GRIB2: codes_set_missing doesn't work for timeIncrement and indicatorOfUnitOfTimeIncrement keys

This commit is contained in:
Shahram Najm 2023-12-07 15:46:03 +00:00
parent 1183031e71
commit 591f335cdb
9 changed files with 257 additions and 8 deletions

View File

@ -196,6 +196,7 @@ int main(int argc, char** argv)
/* 255 = Missing (grib2/tables/4/4.5.table) */
CODES_CHECK(codes_set_long(h, "typeOfSecondFixedSurface", 255), 0);
CODES_CHECK(codes_set_missing(h, "typeOfSecondFixedSurface"), 0);
CODES_CHECK(codes_set_missing(h, "scaleFactorOfSecondFixedSurface"), 0);
CODES_CHECK(codes_set_missing(h, "scaledValueOfSecondFixedSurface"), 0);

View File

@ -340,6 +340,9 @@ void grib_smart_table_delete(grib_context* c);
/* grib_accessor_class_codetable.cc*/
void grib_codetable_delete(grib_context* c);
int codes_codetable_get_contents_malloc(const grib_handle* h, const char* key, code_table_entry** entries, size_t* num_entries);
int codes_codetable_check_code_figure(const grib_handle* h, const char* key, long code_figure);
int codes_codetable_check_abbreviation(const grib_handle* h, const char* key, const char* abbreviation);
/* grib_accessor_class_codetable_units.cc*/
@ -1144,6 +1147,7 @@ int grib_set_missing(grib_handle* h, const char* name);
int grib_is_missing_long(grib_accessor* a, long x);
int grib_is_missing_double(grib_accessor* a, double x);
int grib_is_missing_string(grib_accessor* a, const unsigned char* x, size_t len);
int grib_accessor_can_be_missing(grib_accessor* a, int* err);
int grib_accessor_is_missing(grib_accessor* a, int* err);
int grib_is_missing(const grib_handle* h, const char* name, int* err);
int grib_is_defined(const grib_handle* h, const char* name);

View File

@ -50,7 +50,7 @@ static void thread_init()
CLASS = accessor
SUPER = grib_accessor_class_unsigned
IMPLEMENTS = init;dump;unpack_string;pack_expression;unpack_long
IMPLEMENTS = value_count;pack_string; destroy; get_native_type;
IMPLEMENTS = value_count;pack_string; destroy; get_native_type;pack_missing
MEMBERS = const char* tablename
MEMBERS = const char* masterDir
MEMBERS = const char* localDir
@ -71,6 +71,7 @@ or edit "accessor.class" and rerun ./make_class.pl
*/
static int get_native_type(grib_accessor*);
static int pack_missing(grib_accessor*);
static int pack_string(grib_accessor*, const char*, size_t* len);
static int pack_expression(grib_accessor*, grib_expression*);
static int unpack_long(grib_accessor*, long* val, size_t* len);
@ -115,7 +116,7 @@ static grib_accessor_class _grib_accessor_class_codetable = {
0, /* get offset to bytes */
&get_native_type, /* get native type */
0, /* get sub_section */
0, /* pack_missing */
&pack_missing, /* pack_missing */
0, /* is_missing */
0, /* pack_long */
&unpack_long, /* unpack_long */
@ -523,6 +524,94 @@ void grib_codetable_delete(grib_context* c)
}
}
int codes_codetable_get_contents_malloc(const grib_handle* h, const char* key, code_table_entry** entries, size_t* num_entries)
{
long lvalue = 0;
size_t size = 1;
int err = 0;
grib_context* c = h->context;
grib_accessor* aa = grib_find_accessor(h, key);
if (!aa) return GRIB_NOT_FOUND;
if (!STR_EQUAL(aa->cclass->name, "codetable")) {
return GRIB_INVALID_ARGUMENT; // key is not a codetable
}
const grib_accessor_codetable* ca = (const grib_accessor_codetable*)aa; // could be dynamic_cast
// Decode the key itself. This will either fetch it from the cache or place it there
if ((err = grib_unpack_long(aa, &lvalue, &size)) != GRIB_SUCCESS) {
return err;
}
const grib_codetable* table = ca->table;
if (!table) return GRIB_INTERNAL_ERROR;
grib_codetable* cached_table = c->codetable; // Access the codetable cache
while (cached_table) {
if (STR_EQUAL(table->recomposed_name[0], cached_table->recomposed_name[0])) {
// Found a cache entry that matches the recomposed name of ours
*num_entries = cached_table->size;
*entries = (code_table_entry*)calloc(cached_table->size, sizeof(code_table_entry));
if (!*entries) {
return GRIB_OUT_OF_MEMORY;
}
for (size_t i = 0; i < cached_table->size; i++) {
(*entries)[i] = cached_table->entries[i];
}
return GRIB_SUCCESS;
}
cached_table = cached_table->next;
}
return GRIB_CODE_NOT_FOUND_IN_TABLE;
}
int codes_codetable_check_code_figure(const grib_handle* h, const char* key, long code_figure)
{
code_table_entry* entries = NULL;
size_t num_entries = 0;
int err = 0;
err = codes_codetable_get_contents_malloc(h, key, &entries, &num_entries);
if (err) return err;
if (code_figure < 0 || (size_t)code_figure >= num_entries) {
err = GRIB_OUT_OF_RANGE;
goto cleanup;
}
if (entries[code_figure].abbreviation == NULL) {
err = GRIB_INVALID_KEY_VALUE;
goto cleanup;
}
cleanup:
free(entries);
return err;
}
int codes_codetable_check_abbreviation(const grib_handle* h, const char* key, const char* abbreviation)
{
code_table_entry* entries = NULL;
size_t num_entries = 0;
int err = 0;
err = codes_codetable_get_contents_malloc(h, key, &entries, &num_entries);
if (err) return err;
bool found = false;
for (size_t i=0; i<num_entries; ++i) {
const char* abbrev = entries[i].abbreviation;
if (abbrev && STR_EQUAL(abbrev, abbreviation)) {
found = true;
break;
}
}
if (!found) err = GRIB_INVALID_KEY_VALUE;
free(entries);
return err;
}
static void dump(grib_accessor* a, grib_dumper* dumper)
{
grib_accessor_codetable* self = (grib_accessor_codetable*)a;
@ -824,3 +913,27 @@ static int unpack_long(grib_accessor* a, long* val, size_t* len)
*len = rlen;
return GRIB_SUCCESS;
}
static int pack_missing(grib_accessor* a)
{
// Many of the code tables do have a 'Missing' entry (all bits = 1)
// So it is more user-friendly to allow setting codetable keys to
// missing. For tables that do not have such an entry, an error is issued
grib_accessor_codetable* self = (grib_accessor_codetable*)a;
grib_handle* h = grib_handle_of_accessor(a);
const long nbytes = a->length;
const long nbits = nbytes*8;
const long maxVal = (1<<nbits) - 1;
int err = codes_codetable_check_code_figure(h, a->name, maxVal);
if (!err) {
size_t l = 1;
return grib_pack_long(a, &maxVal, &l);
}
grib_context_log(a->context, GRIB_LOG_ERROR, "There is no 'missing' entry in Code Table %s (%s)",
self->tablename, grib_get_error_message(err));
return err;
}

View File

@ -113,8 +113,6 @@ static void default_long_value(grib_dumper* d, grib_accessor* a, long actualValu
return;
const int type = grib_expression_native_type(h, expression);
DEBUG_ASSERT(type == GRIB_TYPE_LONG);
if (type == GRIB_TYPE_LONG) {
long defaultValue = 0;
if (grib_expression_evaluate_long(h, expression, &defaultValue) == GRIB_SUCCESS && defaultValue != actualValue) {

View File

@ -571,7 +571,7 @@ int grib_set_missing(grib_handle* h, const char* name)
if (a->flags & GRIB_ACCESSOR_FLAG_READ_ONLY)
return GRIB_READ_ONLY;
if (a->flags & GRIB_ACCESSOR_FLAG_CAN_BE_MISSING) {
if (grib_accessor_can_be_missing(a, &ret)) {
if (h->context->debug)
fprintf(stderr, "ECCODES DEBUG grib_set_missing %s\n", name);
@ -641,6 +641,19 @@ int grib_accessor_is_missing(grib_accessor* a, int* err)
}
}
int grib_accessor_can_be_missing(grib_accessor* a, int* err)
{
if (a->flags & GRIB_ACCESSOR_FLAG_CAN_BE_MISSING) {
return 1;
}
if (STR_EQUAL(a->cclass->name, "codetable")) {
// Special case of Code Table keys
// The vast majority have a 'Missing' entry
return 1;
}
return 0;
}
int grib_is_missing(const grib_handle* h, const char* name, int* err)
{
grib_accessor* a = grib_find_accessor(h, name);

View File

@ -50,6 +50,7 @@ list(APPEND test_c_bins
codes_set_samples_path
codes_compare_keys
codes_dump_content
codes_codetable
grib_sh_ieee64
grib_ieee
grib_set_bytes
@ -243,6 +244,7 @@ if( HAVE_BUILD_TOOLS )
grib_set_force
bufr_ecc-556
codes_ecc-1698
codes_codetable
gts_get
gts_ls
gts_count

80
tests/codes_codetable.cc Normal file
View File

@ -0,0 +1,80 @@
/*
* (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 <stdio.h>
#include "grib_api_internal.h"
#undef NDEBUG
#include <assert.h>
int main(int argc, char* argv[])
{
Assert(argc == 1);
grib_handle* h = grib_handle_new_from_samples(0, "GRIB2");
code_table_entry* entries = NULL;
size_t num_entries = 0;
int err = codes_codetable_get_contents_malloc(h, "indicatorOfUnitOfTimeRange", &entries, &num_entries);
Assert(!err);
Assert(entries != NULL);
Assert(num_entries == 256);
for (size_t i=0; i<num_entries;++i) {
const char* abbrev = entries[i].abbreviation;
const char* title = entries[i].title;
if (abbrev) {
Assert(title != NULL);
printf(" i=%zu |%s| |%s|\n", i, abbrev, title);
} else {
Assert(title == NULL);
}
}
Assert( STR_EQUAL(entries[13].abbreviation, "s") );
Assert( STR_EQUAL(entries[13].title, "Second") );
free(entries);
entries = NULL;
// Check a given code is in the table
err = codes_codetable_check_code_figure(h, "indicatorOfUnitOfTimeRange", 7); //century
Assert(err == GRIB_SUCCESS);
err = codes_codetable_check_code_figure(h, "indicatorOfUnitOfTimeRange", 255); //missing
Assert(err == GRIB_SUCCESS);
err = codes_codetable_check_code_figure(h, "indicatorOfUnitOfTimeRange", -1); //-ve code
Assert(err == GRIB_OUT_OF_RANGE);
err = codes_codetable_check_code_figure(h, "indicatorOfUnitOfTimeRange", 666); //out of bounds
Assert(err == GRIB_OUT_OF_RANGE);
err = codes_codetable_check_code_figure(h, "indicatorOfUnitOfTimeRange", 200); // entry not present
Assert(err == GRIB_INVALID_KEY_VALUE);
err = codes_codetable_check_code_figure(h, "American Pie", 0); // non-existent key
Assert(err == GRIB_NOT_FOUND);
err = codes_codetable_check_code_figure(h, "year", 0); // not a codetable key
Assert(err == GRIB_INVALID_ARGUMENT);
// Check a given abbreviation is in the table
err = codes_codetable_check_abbreviation(h, "indicatorOfUnitOfTimeRange", "15m"); // entry not present
Assert(err == GRIB_INVALID_KEY_VALUE);
err = codes_codetable_check_abbreviation(h, "indicatorOfUnitOfTimeRange", "D"); // Day
Assert(err == GRIB_SUCCESS);
err = codes_codetable_check_abbreviation(h, "centre", "ecmf");
Assert(err == GRIB_SUCCESS);
err = codes_codetable_check_abbreviation(h, "centre", "Smoke On The Water"); // non-existent key
Assert(err == GRIB_INVALID_KEY_VALUE);
// Now try a codetable key with 2 octets
err = codes_codetable_get_contents_malloc(h, "gridDefinitionTemplateNumber", &entries, &num_entries);
Assert(!err);
Assert(entries != NULL);
Assert(num_entries == 65536);
Assert( STR_EQUAL(entries[40].title, "Gaussian latitude/longitude") );
free(entries);
grib_handle_delete(h);
return 0;
}

22
tests/codes_codetable.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/sh
# (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.ctest.sh
REDIRECT=/dev/null
label="codes_codetable_test"
tempGrib=temp.$label.grib
tempLog=temp.$label.log
$EXEC ${test_dir}/codes_codetable
# Clean up
rm -f $tempGrib $tempLog

View File

@ -29,10 +29,26 @@ scaleFactorOfSecondFixedSurface=`${tools_dir}/grib_get -w count=1 -p scaleFactor
scaledValueOfSecondFixedSurface=`${tools_dir}/grib_get -w count=1 -p scaledValueOfSecondFixedSurface $outfile`
[ "$scaledValueOfSecondFixedSurface" = "MISSING" ]
set +e
# Codetable keys being set to 'missing'
# -----------------------------------------
sample1=$ECCODES_SAMPLES_PATH/GRIB1.tmpl
sample2=$ECCODES_SAMPLES_PATH/GRIB2.tmpl
temp=temp.grib_missing.grib
${tools_dir}/grib_set -s centre=missing $infile $outfile 2> $REDIRECT > $REDIRECT
# Make sure it works with the default sample
${tools_dir}/grib_set -s typeOfFirstFixedSurface=missing $sample2 $outfile
grib_check_key_equals $outfile 'typeOfFirstFixedSurface:i' '255'
[ $? -ne 0 ]
# Make sure it works with the latest GRIB2 version code table 4.5
latest=`${tools_dir}/grib_get -p tablesVersionLatest $sample2`
${tools_dir}/grib_set -s tablesVersion=$latest $sample2 $temp
${tools_dir}/grib_set -s typeOfFirstFixedSurface=missing $temp $outfile
grib_check_key_equals $outfile 'typeOfFirstFixedSurface:i' '255'
rm -f $temp
${tools_dir}/grib_set -s centre=missing $sample1 $outfile
grib_check_key_equals $outfile 'centre' 'consensus'
# Clean up
rm -f $outfile