Repository: ioos/compliance-checker

Issue:
CF Grid Mapping Required Attributes
The CF-1.7 checker incorrectly requires all attributes listed when only some are applicable. 

[Appendix F](http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#appendix-grid-mappings) describes the grid mapping specifications:

> Each recognized grid mapping is described in one of the sections below. Each section contains: the valid name that is used with the grid_mapping_name attribute; a list of the specific attributes that may be used to assign values to the mapping’s parameters; the standard names used to identify the coordinate variables that contain the mapping’s independent variables; and references to the mapping’s definition or other information that may help in using the mapping. Since the attributes used to set a mapping’s parameters may be shared among several mappings, their definitions are contained in a table in the final section. The attributes which describe the ellipsoid and prime meridian may be included, when applicable, with any grid mapping.

The operative phrase is
> a list of the specific attributes that __may__ be used to assign values to the mapping’s parameters

The checker's incorrect behavior is illustrated in the following example, testing the `lambert_conformal_conic` grid mapping (output shown):

```
§5.6 Horizontal Coorindate Reference Systems, Grid Mappings, Projections
* false_easting is a required attribute for grid mapping lambert_conformal_conic
* false_northing is a required attribute for grid mapping lambert_conformal_conic
```
The spec indicates that the attributes `false_easting` and `false_northing` are valid attributes but not *required* attributes (hence the "...__may__ be used to assign values to the mapping's parameters...").

The correct behavior should only test the conditions for the attributes (found in Table F.1) if the attributes exist.


Discussion:
The usage of `false_easting` and `false_northing` (whether optional or not) is ambiguously described in the CF Conventions 1.7 (see cf-convention/cf-conventions#212). Based on the definition of individual projects it was not clear whether the `false_*` attributes were optional or not. See for example the definition of the [Lambert Conformal projection](http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#_lambert_conformal).

```
grid_mapping_name = lambert_conformal_conic

Map parameters:
- standard_parallel - There may be 1 or 2 values.
- longitude_of_central_meridian
- latitude_of_projection_origin
- false_easting
- false_northing
```

The `false_*` parameters were not marked as optional in the example above. But, `north_pole_grid_longitude` was explicitely defined as optional in the definition of the rotated Lon-Lat grid.

```
grid_mapping_name = rotated_latitude_longitude

Map parameters:
- grid_north_pole_latitude
- grid_north_pole_longitude
- north_pole_grid_longitude - This parameter is option (default is 0).
```

The `false_*` attributes are mandatory based on the individual definitions of the projects -- although the missing `optional`-information is rather a mistake than a deliberate decision.

It will be fixed by issue cf-convention/cf-conventions#212 and the [corresponding pull request](https://github.com/cf-convention/cf-conventions/pull/225), which is due to be accepted.
Thanks for the input, @neumannd. That pull request certainly clears the language up, which is great. Still though, an update will need to be made to the Compliance Checker to reflect the revision, probably in Appendix F:

https://github.com/ioos/compliance-checker/blob/380e16a4f84135605632d75e398564d6885d0de1/compliance_checker/cf/appendix_f.py#L136-L149

The lines with `false_easting` and `false_northing` are currently listed as required attributes, and should be removed (here and wherever else they are actually intended to be optional) to be consistent with the language in your pull request to the spec. Would you agree?
@daltonkell I fully agree
The issue cf-convention/cf-conventions#212 has been solved/accepted and the corresponding pull request cf-convention/cf-conventions#225 has been merged. The modification will be included in the CF Conventions v1.8.
I updated the file `appendix_f.py` and created a pull request. Hope that is OK.

Resolving patch:
diff --git a/compliance_checker/cf/appendix_f.py b/compliance_checker/cf/appendix_f.py
--- a/compliance_checker/cf/appendix_f.py
+++ b/compliance_checker/cf/appendix_f.py
@@ -81,11 +81,12 @@
     'albers_conical_equal_area': [
         (
             'longitude_of_central_meridian',
-            'latitude_of_projection_origin',
+            'latitude_of_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -94,11 +95,12 @@
     'azimuthal_equidistant': [
         (
             'longitude_of_projection_origin',
-            'latitude_of_projection_origin',
+            'latitude_of_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -106,11 +108,12 @@
     ],
     'lambert_cylindrical_equal_area': [
         (
-            'longitude_of_central_meridian',
+            'longitude_of_central_meridian'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -123,11 +126,12 @@
     'lambert_azimuthal_equal_area': [
         (
             'longitude_of_projection_origin',
-            'latitude_of_projection_origin',
+            'latitude_of_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -137,11 +141,12 @@
         (
             'standard_parallel',
             'longitude_of_central_meridian',
-            'latitude_of_projection_origin',
+            'latitude_of_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -157,11 +162,12 @@
     ],
     'mercator': [
         (
-            'longitude_of_projection_origin',
+            'longitude_of_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -174,11 +180,12 @@
     'orthographic': [
         (
             'longitude_of_projection_origin',
-            'latitude_of_projection_origin',
+            'latitude_of_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -187,11 +194,12 @@
     'polar_stereographic': [
         (
             'straight_vertical_longitude_from_pole',
-            'latitude_of_projection_origin',
+            'latitude_of_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -218,11 +226,12 @@
         (
             'longitude_of_projection_origin',
             'latitude_of_projection_origin',
-            'scale_factor_at_projection_origin',
+            'scale_factor_at_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -232,11 +241,12 @@
         (
             'scale_factor_at_central_meridian',
             'longitude_of_central_meridian',
-            'latitude_of_projection_origin',
+            'latitude_of_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -246,11 +256,12 @@
         (
             'longitude_of_projection_origin',
             'latitude_of_projection_origin',
-            'perspective_point_height',
+            'perspective_point_height'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -264,9 +275,15 @@
         (
             'latitude_of_projection_origin',
             'longitude_of_projection_origin',
-            'perspective_point_height',
+            'perspective_point_height'
+        ),
+        (
             'false_easting',
             'false_northing'
+        ),
+        (
+            'projection_x_coordinate',
+            'projection_y_coordinate'
         )
     ],
     'oblique_mercator': [
@@ -274,16 +291,28 @@
             'azimuth',
             'latitude_of_projection_origin',
             'longitude_of_projection_origin',
-            'scale_factor_at_projection_origin',
+            'scale_factor_at_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
+        ),
+        (
+            'projection_x_coordinate',
+            'projection_y_coordinate'
         )
     ],
     'sinusoidal': [
         (
-            'longitude_of_projection_origin',
+            'longitude_of_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
+        ),
+        (
+            'projection_x_coordinate',
+            'projection_y_coordinate'
         )
     ]
 })


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
@@ -929,7 +929,7 @@ def test_check_grid_mapping(self):
 
         assert len(results) == 6
         assert len([r.value for r in results.values()
-                    if r.value[0] < r.value[1]]) == 1
+                    if r.value[0] < r.value[1]]) == 0
         expected_name = u'§5.6 Horizontal Coorindate Reference Systems, Grid Mappings, Projections'
         assert all(r.name == expected_name for r in results.values())