Repository: ioos/compliance-checker (python)

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.

Pull request:
grid mapping false east/north optional; fixes #716
* grid mapping false east/north optional; fixes #716: Attributes false_easting and false_northing are now optional for grid mapping variables. Previously, they were mandatory. The `optional` is in agreement with CF-1.8. In CF version prior to 1.8 it was ambiguous whether these attributes were mandatory or not.
* added projections_*_coordinate to new projections
* updated grid mapping test according to changes

Patch:
diff --git a/compliance_checker/cf/appendix_f.py b/compliance_checker/cf/appendix_f.py
index 651505a..41cc511 100644
--- a/compliance_checker/cf/appendix_f.py
+++ b/compliance_checker/cf/appendix_f.py
@@ -81,11 +81,12 @@ grid_mapping_dict16 = {
     '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 @@ grid_mapping_dict16 = {
     '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 @@ grid_mapping_dict16 = {
     ],
     '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 @@ grid_mapping_dict16 = {
     '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 @@ grid_mapping_dict16 = {
         (
             '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 @@ grid_mapping_dict16 = {
     ],
     'mercator': [
         (
-            'longitude_of_projection_origin',
+            'longitude_of_projection_origin'
+        ),
+        (
             'false_easting',
             'false_northing'
         ),
-        (),
         (
             'projection_x_coordinate',
             'projection_y_coordinate'
@@ -174,11 +180,12 @@ grid_mapping_dict16 = {
     '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 @@ grid_mapping_dict16 = {
     '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 @@ grid_mapping_dict16 = {
         (
             '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 @@ grid_mapping_dict16 = {
         (
             '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 @@ grid_mapping_dict16 = {
         (
             '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 @@ grid_mapping_dict17.update({
         (
             '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 @@ grid_mapping_dict17.update({
             '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
index 6f86bee..d525ffc 100644
--- a/compliance_checker/tests/test_cf.py
+++ b/compliance_checker/tests/test_cf.py
@@ -929,7 +929,7 @@ class TestCF1_6(BaseTestCase):
 
         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())