Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GRIB: apply a heuristics to auto-fix wrong registration of latitudeOfFirstGridPoint in products from Tokyo center #11595

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading