Repository: ioos/compliance-checker

Issue:
CF1.6 checks: issues with the longitudinal coordinate variable might be printed as "§4.1 Latitude Coordinates" issues
# Versions

IOOS compliance checker currently running at https://compliance.ioos.us .


# Problem description

The function `test_longitude` in `test_cf.py` performs some checks on the longitudinal coordinate variable. If a test fails, `§4.1 Latitude Coordinates` is prepended the the warning / error message (line 585 in `test_cf.py`). However, it should be `§4.2 Longitude Coordinate`. 

Additionally, the `s` should be removed in the `§4.1 Latitude Coordinates` of the function `test_latitude`.


# Reproduction of the issue

Generate a NetCDF file from this CDL code:

```
netcdf ex_5.3 {
dimensions:
  londim = 128 ;
  latdim = 64 ;
  rgrid = 6144 ;
variables:
  float PS(rgrid) ;
    PS:long_name = "surface pressure" ;
    PS:units = "Pa" ;
    PS:coordinates = "lon lat" ;
  float lon(rgrid) ;
    lon:long_name = "longitude" ;
    lon:units = "degrees_east" ;
  float lat(rgrid) ;
    lat:long_name = "latitude" ;
    lat:units = "degrees_north" ;
  int rgrid(rgrid);
    rgrid:compress = "latdim londim";
}
```

To generate a NetCDF file from this text, copy it into a text file `ex_5.3.cdl` and run on the command line:

```
ncgen ex_5.3.cdl -o ex_5.3.nc
```

`ncgen` is part of the command line tools delivered with the NetCDF libraries.


# Solution

I will create a pull request in a few minutes solving it.


Discussion:

Resolving patch:
diff --git a/compliance_checker/cf/cf.py b/compliance_checker/cf/cf.py
--- a/compliance_checker/cf/cf.py
+++ b/compliance_checker/cf/cf.py
@@ -100,8 +100,8 @@ def __init__(self):
             "3.4": "§3.4 Ancillary Data",
             "3.5": "§3.5 Flags",
             "4"  : "§4 Coordinate Types",
-            "4.1": "§4.1 Latitude Coordinates",
-            "4.2": "§4.2 Longitude Coordinates",
+            "4.1": "§4.1 Latitude Coordinate",
+            "4.2": "§4.2 Longitude Coordinate",
             "4.3": "§4.3 Vertical Coordinate",
             "4.4": "§4.4 Time Coordinate",
             "4.5": "§4.5 Discrete Axis",
@@ -1706,13 +1706,13 @@ def check_longitude(self, ds):
 
             # NOTE see docstring--should below be 4.1 or 4.2?
             # Check that longitude defines units
-            valid_longitude = TestCtx(BaseCheck.HIGH, self.section_titles['4.1'])
+            valid_longitude = TestCtx(BaseCheck.HIGH, self.section_titles['4.2'])
             valid_longitude.assert_true(units is not None,
                                         "longitude variable '{}' must define units".format(longitude))
             ret_val.append(valid_longitude.to_result())
 
             # Check that longitude uses allowed units
-            allowed_units = TestCtx(BaseCheck.MEDIUM, self.section_titles['4.1'])
+            allowed_units = TestCtx(BaseCheck.MEDIUM, self.section_titles['4.2'])
             if standard_name == 'grid_longitude':
                 e_n_units = cfutil.VALID_LAT_UNITS | cfutil.VALID_LON_UNITS
                 # check that the units aren't in east and north degrees units,
@@ -1735,13 +1735,13 @@ def check_longitude(self, ds):
                        "".format(longitude))
                 recommended_units = Result(BaseCheck.LOW,
                                            (1, 1),
-                                           self.section_titles['4.1'], [msg])
+                                           self.section_titles['4.2'], [msg])
                 ret_val.append(recommended_units)
 
             x_variables = ds.get_variables_by_attributes(axis='X')
             # Check that longitude defines either standard_name or axis
-            definition = TestCtx(BaseCheck.MEDIUM, self.section_titles['4.1'])
-            definition.assert_true(standard_name == 'longitude' or axis == 'Y' or x_variables != [],
+            definition = TestCtx(BaseCheck.MEDIUM, self.section_titles['4.2'])
+            definition.assert_true(standard_name == 'longitude' or axis == 'X' or x_variables != [],
                                    "longitude variable '{}' should define standard_name='longitude' or axis='X'"
                                    "".format(longitude))
             ret_val.append(definition.to_result())


Test patch:
diff --git a/compliance_checker/tests/test_cf.py b/compliance_checker/tests/test_cf.py
--- a/compliance_checker/tests/test_cf.py
+++ b/compliance_checker/tests/test_cf.py
@@ -539,7 +539,7 @@ def test_latitude(self):
         assert len(results) == 12
         assert scored < out_of
         assert len([r for r in results if r.value[0] < r.value[1]]) == 3
-        assert (r.name == u'§4.1 Latitude Coordinates' for r in results)
+        assert (r.name == u'§4.1 Latitude Coordinate' for r in results)
 
         # check with another ds -- all 6 vars checked pass
         dataset = self.load_dataset(STATIC_FILES['rotated_pole_grid'])
@@ -547,7 +547,7 @@ def test_latitude(self):
         scored, out_of, messages = self.get_results(results)
         assert len(results) == 6
         assert scored == out_of
-        assert (r.name == u'§4.1 Latitude Coordinates' for r in results)
+        assert (r.name == u'§4.1 Latitude Coordinate' for r in results)
 
         # hack to avoid writing to read-only file
         dataset.variables['rlat'] = MockVariable(dataset.variables['rlat'])
@@ -582,7 +582,7 @@ def test_longitude(self):
         assert len(results) == 12
         assert scored < out_of
         assert len([r for r in results if r.value[0] < r.value[1]]) == 3
-        assert all(r.name == u'§4.1 Latitude Coordinates' for r in results)
+        assert all(r.name == u'§4.2 Longitude Coordinate' for r in results)
 
         # check different dataset # TODO can be improved for check_latitude too
         dataset = self.load_dataset(STATIC_FILES['rotated_pole_grid'])