Skip to content

Commit

Permalink
netCDF: add support to identify a geolocation array for a variable th…
Browse files Browse the repository at this point in the history
…at lacks a 'coordinates' attribute

but that has side "lon" and "lat" 2D variables whose dimensions are the same as the last 2 ones of the variable of interest

Fixes https://lists.osgeo.org/pipermail/gdal-dev/2024-December/059870.html
  • Loading branch information
rouault committed Dec 3, 2024
1 parent 671c0ed commit 62a24fb
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 19 deletions.
Binary file not shown.
19 changes: 19 additions & 0 deletions autotest/gdrivers/netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6615,3 +6615,22 @@ def test_netcdf_write_check_golden_file(
)
assert os.stat(golden_file).st_size == os.stat(out_filename).st_size
assert open(golden_file, "rb").read() == open(out_filename, "rb").read()


###############################################################################
# Test we identify a geolocation array for a variable that lacks a
# "coordinates" attribute, but that has side "lon" and "lat" 2D variables
# whose dimensions are the same as the last 2 ones of the variable of interest


def test_netcdf_var_with_geoloc_array_but_no_coordinates_attr():

ds = gdal.Open(
"NETCDF:data/netcdf/var_with_geoloc_array_but_no_coordinates_attr.nc:NO2"
)
got_md = ds.GetMetadata("GEOLOCATION")
assert got_md
assert (
got_md["X_DATASET"]
== 'NETCDF:"data/netcdf/var_with_geoloc_array_but_no_coordinates_attr.nc":lon'
)
86 changes: 67 additions & 19 deletions frmts/netcdf/netcdfdataset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4733,46 +4733,95 @@ int netCDFDataset::ProcessCFGeolocation(int nGroupId, int nVarId,
std::string &osGeolocYNameOut)
{
bool bAddGeoloc = false;
char *pszTemp = nullptr;
char *pszCoordinates = nullptr;

// If there is no explicit "coordinates" attribute, tries to find if
// there are "lon" and "lat" 2D variables whose dimensions are the last
// 2 ones of the variable of interest.
if (NCDFGetAttr(nGroupId, nVarId, "coordinates", &pszCoordinates) !=
CE_None)
{
CPLFree(pszCoordinates);
pszCoordinates = nullptr;

int nVarDims = 0;
NCDF_ERR(nc_inq_varndims(nGroupId, nVarId, &nVarDims));
if (nVarDims >= 2)
{
std::vector<int> anVarDimIds(nVarDims);
NCDF_ERR(nc_inq_vardimid(nGroupId, nVarId, anVarDimIds.data()));

int nLongitudeId = 0;
int nLatitudeId = 0;
if (nc_inq_varid(nGroupId, "lon", &nLongitudeId) == NC_NOERR &&
nc_inq_varid(nGroupId, "lat", &nLatitudeId) == NC_NOERR)
{
int nDimsLongitude = 0;
NCDF_ERR(
nc_inq_varndims(nGroupId, nLongitudeId, &nDimsLongitude));
int nDimsLatitude = 0;
NCDF_ERR(
nc_inq_varndims(nGroupId, nLatitudeId, &nDimsLatitude));
if (nDimsLongitude == 2 && nDimsLatitude == 2)
{
std::vector<int> anDimLongitudeIds(2);
NCDF_ERR(nc_inq_vardimid(nGroupId, nLongitudeId,
anDimLongitudeIds.data()));
std::vector<int> anDimLatitudeIds(2);
NCDF_ERR(nc_inq_vardimid(nGroupId, nLatitudeId,
anDimLatitudeIds.data()));
if (anDimLongitudeIds == anDimLatitudeIds &&
anVarDimIds[anVarDimIds.size() - 2] ==
anDimLongitudeIds[0] &&
anVarDimIds[anVarDimIds.size() - 1] ==
anDimLongitudeIds[1])
{
pszCoordinates = CPLStrdup("lon lat");
}
}
}
}
}

if (NCDFGetAttr(nGroupId, nVarId, "coordinates", &pszTemp) == CE_None)
if (pszCoordinates)
{
// Get X and Y geolocation names from coordinates attribute.
char **papszTokens = NCDFTokenizeCoordinatesAttribute(pszTemp);
if (CSLCount(papszTokens) >= 2)
const CPLStringList aosCoordinates(
NCDFTokenizeCoordinatesAttribute(pszCoordinates));
if (aosCoordinates.size() >= 2)
{
char szGeolocXName[NC_MAX_NAME + 1];
char szGeolocYName[NC_MAX_NAME + 1];
szGeolocXName[0] = '\0';
szGeolocYName[0] = '\0';

// Test that each variable is longitude/latitude.
for (int i = 0; i < CSLCount(papszTokens); i++)
for (int i = 0; i < aosCoordinates.size(); i++)
{
if (NCDFIsVarLongitude(nGroupId, -1, papszTokens[i]))
if (NCDFIsVarLongitude(nGroupId, -1, aosCoordinates[i]))
{
int nOtherGroupId = -1;
int nOtherVarId = -1;
// Check that the variable actually exists
// Needed on Sentinel-3 products
if (NCDFResolveVar(nGroupId, papszTokens[i], &nOtherGroupId,
&nOtherVarId) == CE_None)
if (NCDFResolveVar(nGroupId, aosCoordinates[i],
&nOtherGroupId, &nOtherVarId) == CE_None)
{
snprintf(szGeolocXName, sizeof(szGeolocXName), "%s",
papszTokens[i]);
aosCoordinates[i]);
}
}
else if (NCDFIsVarLatitude(nGroupId, -1, papszTokens[i]))
else if (NCDFIsVarLatitude(nGroupId, -1, aosCoordinates[i]))
{
int nOtherGroupId = -1;
int nOtherVarId = -1;
// Check that the variable actually exists
// Needed on Sentinel-3 products
if (NCDFResolveVar(nGroupId, papszTokens[i], &nOtherGroupId,
&nOtherVarId) == CE_None)
if (NCDFResolveVar(nGroupId, aosCoordinates[i],
&nOtherGroupId, &nOtherVarId) == CE_None)
{
snprintf(szGeolocYName, sizeof(szGeolocYName), "%s",
papszTokens[i]);
aosCoordinates[i]);
}
}
}
Expand Down Expand Up @@ -4840,26 +4889,25 @@ int netCDFDataset::ProcessCFGeolocation(int nGroupId, int nVarId,
"cannot resolve location of "
"lat/lon variables specified by the coordinates "
"attribute [%s]",
pszTemp);
pszCoordinates);
}
CPLFree(pszGeolocXFullName);
CPLFree(pszGeolocYFullName);
}
else
{
CPLDebug("GDAL_netCDF",
"coordinates attribute [%s] is unsupported", pszTemp);
"coordinates attribute [%s] is unsupported",
pszCoordinates);
}
}
else
{
CPLDebug("GDAL_netCDF",
"coordinates attribute [%s] with %d element(s) is "
"unsupported",
pszTemp, CSLCount(papszTokens));
pszCoordinates, aosCoordinates.size());
}
if (papszTokens)
CSLDestroy(papszTokens);
}

else
Expand All @@ -4870,7 +4918,7 @@ int netCDFDataset::ProcessCFGeolocation(int nGroupId, int nVarId,
bAddGeoloc = ProcessNASAEMITGeoLocation(nGroupId, nVarId);
}

CPLFree(pszTemp);
CPLFree(pszCoordinates);

return bAddGeoloc;
}
Expand Down

0 comments on commit 62a24fb

Please sign in to comment.