Skip to content

Commit 883763f

Browse files
authored
Merge pull request #13888 from OSGeo/backport-13834-to-release/3.12
[Backport release/3.12] netCDF: use stored GeoTransform attribute only if consistent with dimension variables
2 parents da75a2f + f20bcad commit 883763f

File tree

3 files changed

+86
-38
lines changed

3 files changed

+86
-38
lines changed

autotest/gdrivers/netcdf.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6802,6 +6802,30 @@ def test_netcdf_geotransform_preserved_createcopy(tmp_path):
68026802
assert dst.GetGeoTransform() == src.GetGeoTransform()
68036803

68046804

6805+
###############################################################################
6806+
# Test that the GeoTransform attribute is ignored if it does not appear to
6807+
# correspond with the dimension variables.
6808+
# https://github.com/OSGeo/gdal/issues/13823
6809+
6810+
6811+
def test_netcdf_geotranform_ignored_if_erroneous(tmp_path):
6812+
6813+
with gdaltest.error_raised(
6814+
gdal.CE_Warning,
6815+
"GeoTransform read from attribute of transverse_mercator variable differs from value calculated from dimension variables",
6816+
):
6817+
ds = gdal.Open("data/netcdf/uint.nc")
6818+
6819+
assert (
6820+
ds.GetMetadataItem("transverse_mercator#GeoTransform")
6821+
== "440720 60 0 3751320 0 -60 "
6822+
)
6823+
6824+
gt = ds.GetGeoTransform()
6825+
6826+
assert gt == (440720.0, 60.0, 0.0, 3750240.0, 0.0, -60.0)
6827+
6828+
68056829
###############################################################################
68066830
#
68076831

doc/source/drivers/raster/netcdf.rst

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Driver capabilities
3030
Multiple Image Handling (Subdatasets)
3131
-------------------------------------
3232

33-
Network Command Data Form is a container for several different arrays
33+
Network Common Data Form is a container for several different arrays
3434
most used for storing scientific dataset. One NetCDF file may contain
3535
several datasets. They may differ in size, number of dimensions and may
3636
represent data for different regions.
@@ -228,48 +228,66 @@ accepting it, or ``netCDF`` as the only value of the ``papszAllowedDrivers`` of
228228
filename, when it is not using subdataset syntax (it can typically be used to
229229
force open a HDF5 file that would be nominally recognized by the HDF5 driver).
230230

231-
Dimension
232-
---------
231+
Dimension Ordering
232+
------------------
233233

234-
The NetCDF driver assume that data follows the CF-1 convention from
235-
`UNIDATA <http://www.unidata.ucar.edu/software/netcdf/docs/conventions.html>`__
236-
The dimensions inside the NetCDF file use the following rules: (Z,Y,X).
234+
The NetCDF driver assume that dimensions are ordered as (Z/T,Y,X), according to the recommendations of the `CF Conventions <https://cfconventions.org>`__.
237235
If there are more than 3 dimensions, the driver will merge them into
238-
bands. For example if you have an 4 dimension arrays of the type (P, T,
239-
Y, X). The driver will multiply the last 2 dimensions (P*T). The driver
240-
will display the bands in the following order. It will first increment T
241-
and then P. Metadata will be displayed on each band with its
236+
bands. For example, if you have a 4-dimensional array with dimensions (P, T,
237+
Y, X), the driver will combine the last 2 dimensions (P*T) into bands.
238+
Bands will be ordered by first incrementing T and then P.
239+
Metadata will be displayed on each band with its
242240
corresponding T and P values.
243241

244-
Georeference
245-
------------
242+
Georeferencing
243+
--------------
244+
245+
Spatial reference system
246+
++++++++++++++++++++++++
247+
248+
There are multiple ways of storing spatial reference systems in netCDF files.
249+
Under the CF conventions, each data variable may use a ``grid_mapping`` attribute
250+
to reference the name of a separate "grid mapping" variable, such as ``crs``, whose attributes
251+
in turn specify parameters of the spatial reference system. Because the
252+
parameters recognized by the CF conventions (``semi_major_axis``, ``inverse_flattening``, ``standard_parallel``, etc.) may not be adequate to fully describe a spatial reference system, the
253+
grid mapping variable may also have a ``crs_wkt`` attribute that fully describes the
254+
spatial reference system using an OGC WKT string. The attribute name ``crs_wkt`` was
255+
not specified until version 1.7 of the CF conventions; before that time, GDAL used
256+
the attribute name ``spatial_ref`` for the same purpose.
246257

247-
There is no universal way of storing georeferencing in NetCDF files. The
248-
driver first tries to follow the CF-1 Convention from UNIDATA looking
249-
for the Metadata named "grid_mapping". If "grid_mapping" is not present,
250-
the driver will try to find an lat/lon grid array to set geotransform
251-
array. The NetCDF driver verifies that the Lat/Lon array is equally
252-
spaced.
258+
When reading a variable from a netCDF file, GDAL first checks the ``grid_mapping`` attribute to find the name of the grid mapping variable. If this variable has a ``spatial_ref`` or ``crs_wkt`` attribute (checked in that order), it will be used to assign a spatial reference system. If not, the spatial reference system will be assigned using using projection parameter attributes (``semi_major_axis``, etc.) If these parameters are not present, GDAL will check the grid mapping variable for a (non-standard) ``srid`` attribute and attempt to interpret it if present.
253259

254-
.. versionadded:: 3.4 crs_wkt attribute support
260+
If no ``grid_mapping`` attribute is present, GDAL will check the data variable for a (non-standard) ``crs`` attribute and read a WKT, EPSG, or PROJ.4 string from it.
255261

256-
If those 2 methods fail, NetCDF driver will try to read the following
257-
metadata directly and set up georeferencing.
262+
If GDAL cannot determine a spatial reference system using the above methods, it will be left undefined unless the dataset was opened with the :oo:`ASSUME_LONGLAT` option.
258263

259-
- spatial_ref (Well Known Text)
264+
Geotransform
265+
++++++++++++
260266

261-
- GeoTransform (GeoTransform array)
267+
NetCDF files store locations for each pixel using coordinate variables
268+
such as ``latitude`` and ``longitude``. This differs from the GDAL :ref:`raster_data_model`, wherein pixel locations are defined relative to an origin point, with fixed resolutions along two axes. The GDAL model uses a six-parameter geotransform to store the origin point, resolutions, and axis
269+
rotations (:ref:`geotransforms_tut`).
262270

263-
or,
271+
If the netCDF file represents a rectangular grid with equal cell sizes, GDAL can derive a geotransform for the dataset by reading the values of the coordinate variables and computing spacings between adjacent pixels. Because of floating-point errors, this geotransform may not match the resolution of the original dataset. To allow the geotransform to be stored exactly, GDAL writes it as an attribute of the grid mapping variable described above. However, this
272+
is a GDAL-specific extension and will not typically be present in files not written by GDAL.
264273

265-
- Northernmost_Northing
266-
- Southernmost_Northing
267-
- Easternmost_Easting
268-
- Westernmost_Easting
274+
If the grid is rectangular but the cell sizes are not equal, as in the case of Gaussian grids used for weather and climate modelling, GDAL will still report a geotransform if the cell sizes are equal within a 0.1-degree threshold. Whether or not a geotransform is reported, the exact values of the cell centers can be accessed via the ``Y_VALUES`` metadata item in the ``GEOLOCATION2`` domain.
269275

270-
See also the configuration options **GDAL_NETCDF_VERIFY_DIMS** and
271-
**GDAL_NETCDF_IGNORE_XY_AXIS_NAME_CHECKS** which control this
276+
If the file does not represent a rectangular grid, GDAL will not report a geotransform. In this case, the ``GEOLOCATION`` metadata domain provides references to the datasets that can be used to read the coordinates associated with each pixel.
277+
278+
When reading a netCDF, GDAL must first identify the coordinate variables associated with a data variable. The configuration options :config:`GDAL_NETCDF_VERIFY_DIMS` and
279+
:config:`GDAL_NETCDF_IGNORE_XY_AXIS_NAME_CHECKS` control this
272280
behavior.
281+
If the coordinate variables are one-dimensional, indicating a possibly regular grid, GDAL will then check whether the coordinate variables values are equally-spaced and, if applicable, calculate a geotransform. Starting with GDAL 3.11,
282+
if the ``GeoTransform`` metadata is also present, the values of the
283+
calculated and stored geotransforms will be compared and an warning
284+
raised if they do not correspond. In such a case, preference will be
285+
given to the calculated values in GDAL 3.12.3 and above, or the stored
286+
values in earlier versions. (A significant mismatch may arise if a file is
287+
created by GDAL, modified using another software package that ignores the
288+
``GeoTransform`` attribute, and then read in again by GDAL.)
289+
290+
An alternative method of storing the geotransform relies on storing the spatial extent of the dataset using four attributes on the grid mapping variable: ``Northermost_Northing``, ``Southernmost_Northing``, ``Eastermost_Easting``, and ``Westernmost_Eastering``. If these attributes are present (and ``GeoTransform`` is not), they will be used instead of the computed values.
273291

274292
Open options
275293
------------
@@ -319,10 +337,10 @@ The following open options are available:
319337
:default: NO
320338
:since: 3.7
321339

322-
Whether a Geographic CRS should
340+
Whether a Geographic CRS (OGC:CRS84) should
323341
be assumed and applied when, none has otherwise been found, a meaningful
324342
geotransform has been found, and that geotransform is within the bounds
325-
-180,360 -90,90, if YES assume OGC:CRS84.
343+
-180,360 -90,90.
326344

327345
- .. oo:: PRESERVE_AXIS_UNIT_IN_CRS
328346
:choices: YES, NO
@@ -620,10 +638,10 @@ Configuration Options
620638
:default: NO
621639
:since: 3.7
622640

623-
Whether a Geographic CRS should
624-
be assumed and applied when, none has otherwise been found, a meaningful
641+
Whether a Geographic CRS (OGC:CRS84) should
642+
be assumed and applied when none has otherwise been found, a meaningful
625643
geotransform has been found, and that geotransform is within the bounds
626-
-180,360 -90,90, if YES assume OGC:CRS84.
644+
-180,360 -90,90. Equivalent to open option :oo:`ASSUME_LONGLAT`.
627645

628646
- .. config:: GDAL_NETCDF_REPORT_EXTRA_DIM_VALUES
629647
:choices: YES, NO

frmts/netcdf/netcdfdataset.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4155,6 +4155,8 @@ void netCDFDataset::SetProjectionFromVar(
41554155
CSLTokenizeString2(pszGeoTransform, " ", CSLT_HONOURSTRINGS));
41564156
if (aosGeoTransform.size() == 6)
41574157
{
4158+
bool bUseGeoTransformFromAttribute = true;
4159+
41584160
GDALGeoTransform gtFromAttribute;
41594161
for (int i = 0; i < 6; i++)
41604162
{
@@ -4180,17 +4182,21 @@ void netCDFDataset::SetProjectionFromVar(
41804182

41814183
if (dfMaxAbsoluteError > 0)
41824184
{
4185+
bUseGeoTransformFromAttribute = false;
41834186
CPLError(CE_Warning, CPLE_AppDefined,
41844187
"GeoTransform read from attribute of %s "
41854188
"variable differs from value calculated from "
41864189
"dimension variables (max diff = %g). Using "
4187-
"value from attribute.",
4190+
"value calculated from dimension variables.",
41884191
pszGridMappingValue, dfMaxAbsoluteError);
41894192
}
41904193
}
41914194

4192-
tmpGT = std::move(gtFromAttribute);
4193-
bGotGdalGT = true;
4195+
if (bUseGeoTransformFromAttribute)
4196+
{
4197+
tmpGT = gtFromAttribute;
4198+
bGotGdalGT = true;
4199+
}
41944200
}
41954201
}
41964202
else

0 commit comments

Comments
 (0)