GribApiDotNet / GribApi.NET

A powerful .NET library for reading and writing GRIB 1 and 2 files
Apache License 2.0
54 stars 28 forks source link

DateTime crashes for grib files with constant values (no time dimension) #40

Closed xavierpena closed 7 years ago

xavierpena commented 7 years ago

The file that fails

Link to the file:

https://donneespubliques.meteofrance.fr/donnees_libres/Static/ARPEGE_0.1_CONSTANT.grib

The link is a grib1 file from MeteoFrance, containing "constant fields (relief and mask earth-sea)". It can be found in their download page, at the bottom of the page.

It contains the elevation for each point of their of the grid for 2 of the MeteoFrance products (AROME and ARPEGE).

Where and why the code crashes

The error message was "Unrepresentable date time". I went and debugged through your source code, and sure enough:

In the file GribMessage.cs, prop ReferenceTime (line 368), one can see how the DateTime can't be build with the parameters provided by the grib1 file, which are:

year=12857; month=255; day=255; hour=255; minute=0; second=0;

The grib file contains two grib messages:

Both of them are static and don't require any date.

Would it be safe to set it like so?

if(day == 255)
{
    year = month = day = 1;
    hour = minute = second = 0; 
}

I don't know if those 255 are a convention or a MeteoFrance particularity.

Problems when debugging

It took me a while to set it up to be able to step into the code when debugging.

I got the following message:

"GribEnvironment::DefinitionsPath must be a valid path."

From the docs, I read that I had to set:

Environment.SetEnvironmentVariable("GRIB_API_DIR_ROOT", "C:\\Some\\Path", EnvironmentVariableTarget.Process);

But it toke me a while what this "C:\\Some\\Path" some path should be. In the git project, under \GribApi: the folders \definitions, \lib, \samples are empty. Plus there was not boot.def at the root, which the code indicated that was required.

So in the end I used the path to the nuget package in another project... and it worked.

I don't know if it's possible to include those files in the git project, so it can be debugged right away without depending on those files that are (I think) only available to the user through the nuget package.

xavierpena commented 7 years ago

The change as proposed (see code below).

Another possibility would be to configure the GribMessage with a flag of constantValue = true which would directly return default(DateTime).

/// <summary>
/// Gets or set the *reference* time of the data - date and time of start of averaging or accumulation period. Time is UTC.
/// </summary>
/// <value>
/// The reference time.
/// </value>
public DateTime ReferenceTime
{
    get
    {
        var year = this["year"].AsInt();
        var month = this["month"].AsInt();
        var day = this["day"].AsInt();
        var hour = this["hour"].AsInt();
        var minute = this["minute"].AsInt();
        var second = this["second"].AsInt();

        if(day == 255)
        {
            year = month = day = 1;
            hour = minute = second = 0;
        }

        return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc);
    }
    set
    {
        this["year"].AsInt(value.Year);
        this["month"].AsInt(value.Month);
        this["day"].AsInt(value.Day);
        this["hour"].AsInt(value.Hour);
        this["minute"].AsInt(value.Minute);
        this["second"].AsInt(value.Second);
    }
}
0x1mason commented 7 years ago

Thanks for the debug info. I will take a look at it.

0x1mason commented 7 years ago

Regarding debugging, I'm getting ready to release v1.0.0. The entire project structure has changed and the native and managed code are in different packages. I'm hoping this will make it easier to work with.

0x1mason commented 7 years ago

I fixed this as follows:

// some grib values do not require a date and this will throw; set a default value
var time = new DateTime(1, 1, 1, 0 ,0 ,0, DateTimeKind.Utc);

try
{
   time = new DateTime(this["year"].AsInt(), this["month"].AsInt(), this["day"].AsInt(),
                                      this["hour"].AsInt(), this["minute"].AsInt(), this["second"].AsInt(),
                      DateTimeKind.Utc);
}
catch (Exception) { }

return time;
0x1mason commented 7 years ago

look for v1.0.1-beta coming up soon