Skip to content

Commit

Permalink
GRIB: apply a heuristics to auto-fix wrong registration of latitudeOf…
Browse files Browse the repository at this point in the history
…FirstGridPoint in products from Tokyo center
  • Loading branch information
rouault committed Jan 7, 2025
1 parent 1646133 commit d7d1f8b
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 6 deletions.
Binary file not shown.
15 changes: 15 additions & 0 deletions autotest/gdrivers/grib.py
Original file line number Diff line number Diff line change
Expand Up @@ -2410,3 +2410,18 @@ def test_grib_grib2_minx_180():
ds = gdal.Open("data/grib/minx_180.grib2")
gt = ds.GetGeoTransform()
assert gt == pytest.approx((-180.0625, 0.125, 0.0, 90.0625, 0.0, -0.125), rel=1e-6)


def test_grib_grib2_MANAL_2023030103_fake_wrong_grid_origin_latitude():
with gdal.quiet_errors():
ds = gdal.Open(
"data/grib/MANAL_2023030103_fake_wrong_grid_origin_latitude.grb2"
)
assert (
gdal.GetLastErrorMsg()
== "Likely buggy grid registration for GRIB2 product: heuristics shows that the latitudeOfFirstGridPoint is likely to qualify the latitude of the northern-most grid point instead of the southern-most grid point as expected. Please report to data producer. This heuristics can be disabled by setting the GRIB_LATITUDE_OF_FIRST_GRID_POINT_IS_SOUTHERN_MOST configuration option to YES."
)
gt = ds.GetGeoTransform()
assert gt == pytest.approx(
(-2442500.0217935005, 5000.0, 0.0, 2042500.0318467868, 0.0, -5000.0), rel=1e-6
)
77 changes: 71 additions & 6 deletions frmts/grib/gribdataset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2656,7 +2656,7 @@ void GRIBDataset::SetGribMetaData(grib_MetaData *meta)
// case of latlon).
rMinX = meta->gds.lon1;
// Latitude in degrees, to be transformed to meters.
rMaxY = meta->gds.lat1;
double dfGridOriY = meta->gds.lat1;

if (m_poSRS == nullptr || m_poLL == nullptr ||
!m_poSRS->IsSame(&oSRS) || !m_poLL->IsSame(&oLL))
Expand All @@ -2666,21 +2666,86 @@ void GRIBDataset::SetGribMetaData(grib_MetaData *meta)
}

// Transform it to meters.
if ((m_poCT != nullptr) && m_poCT->Transform(1, &rMinX, &rMaxY))
if ((m_poCT != nullptr) && m_poCT->Transform(1, &rMinX, &dfGridOriY))
{
if (meta->gds.scan == GRIB2BIT_2) // Y is minY, GDAL wants maxY.
{
// -1 because we GDAL needs the coordinates of the centre of
// the pixel.
rMaxY += (meta->gds.Ny - 1) * meta->gds.Dy;
const char *pszConfigOpt = CPLGetConfigOption(
"GRIB_LATITUDE_OF_FIRST_GRID_POINT_IS_SOUTHERN_MOST",
nullptr);
bool bLatOfFirstPointIsSouthernMost =
!pszConfigOpt || CPLTestBool(pszConfigOpt);

// Hack for a file called MANAL_2023030103.grb2 that
// uses LCC and has Latitude of false origin = 30
// Longitude of false origin = 140
// Latitude of 1st standard parallel = 60
// Latitude of 2nd standard parallel = 30
// but whose (meta->gds.lon1, meta->gds.lat1) qualifies the
// northern-most point of the grid and not the bottom-most one
// as it should given the scan == GRIB2BIT_2
if (!pszConfigOpt && meta->gds.projType == GS3_LAMBERT &&
std::fabs(meta->gds.scaleLat1 - 60) <= 1e-8 &&
std::fabs(meta->gds.scaleLat2 - 30) <= 1e-8 &&
std::fabs(meta->gds.meshLat - 30) <= 1e-8 &&
std::fabs(Lon360to180(meta->gds.orientLon) - 140) <= 1e-8)
{
double dfXCenterProj = Lon360to180(meta->gds.orientLon);
double dfYCenterProj = meta->gds.meshLat;
if (m_poCT->Transform(1, &dfXCenterProj, &dfYCenterProj))
{
double dfXCenterGridNominal =
rMinX + nRasterXSize * meta->gds.Dx / 2;
double dfYCenterGridNominal =
dfGridOriY + nRasterYSize * meta->gds.Dy / 2;
double dfXCenterGridBuggy = dfXCenterGridNominal;
double dfYCenterGridBuggy =
dfGridOriY - nRasterYSize * meta->gds.Dy / 2;
const auto SQR = [](double x) { return x * x; };
if (SQR(dfXCenterGridBuggy - dfXCenterProj) +
SQR(dfYCenterGridBuggy - dfYCenterProj) <
SQR(10) *
(SQR(dfXCenterGridNominal - dfXCenterProj) +
SQR(dfYCenterGridNominal - dfYCenterProj)))
{
CPLError(
CE_Warning, CPLE_AppDefined,
"Likely buggy grid registration for GRIB2 "
"product: heuristics shows that the "
"latitudeOfFirstGridPoint is likely to qualify "
"the latitude of the northern-most grid point "
"instead of the southern-most grid point as "
"expected. Please report to data producer. "
"This heuristics can be disabled by setting "
"the "
"GRIB_LATITUDE_OF_FIRST_GRID_POINT_IS_SOUTHERN_"
"MOST configuration option to YES.");
bLatOfFirstPointIsSouthernMost = false;
}
}
}
if (bLatOfFirstPointIsSouthernMost)
{
// -1 because we GDAL needs the coordinates of the centre of
// the pixel.
rMaxY = dfGridOriY + (meta->gds.Ny - 1) * meta->gds.Dy;
}
else
{
rMaxY = dfGridOriY;
}
}
else
{
rMaxY = dfGridOriY;
}
rPixelSizeX = meta->gds.Dx;
rPixelSizeY = meta->gds.Dy;
}
else
{
rMinX = 0.0;
rMaxY = 0.0;
// rMaxY = 0.0;

rPixelSizeX = 1.0;
rPixelSizeY = -1.0;
Expand Down
1 change: 1 addition & 0 deletions port/cpl_known_config_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ constexpr static const char* const apszKnownConfigOptions[] =
"GRIB_CACHEMAX", // from gribdataset.cpp
"GRIB_DEFAULT_SEMI_MAJOR", // from gribdataset.cpp
"GRIB_DEFAULT_SEMI_MINOR", // from gribdataset.cpp
"GRIB_LATITUDE_OF_FIRST_GRID_POINT_IS_SOUTHERN_MOST", // from gribdataset.cpp
"GRIB_NORMALIZE_UNITS", // from gribdataset.cpp
"GRIB_PDS_ALL_BANDS", // from gribdataset.cpp
"GRIB_RESOURCE_DIR", // from metaname.cpp
Expand Down

0 comments on commit d7d1f8b

Please sign in to comment.