Repository: ioos/compliance-checker
Pull request #1167 (2025-05-02 22:01:35)

Checks for part_node_count when interior_ring attribute defined


Patch:
diff --git a/compliance_checker/cf/cf_1_8.py b/compliance_checker/cf/cf_1_8.py
index f3a2b88..2b592a1 100644
--- a/compliance_checker/cf/cf_1_8.py
+++ b/compliance_checker/cf/cf_1_8.py
@@ -240,6 +240,27 @@ class CF1_8Check(CF1_7Check):
                 ds,
                 getattr(geometry_var, "interior_ring", None),
             )
+            # IMPLEMENTATION CONFORMANCE 7.5 REQUIRED 18/20
+            if interior_ring:
+                geom_valid.out_of += 1
+                if not part_node_count:
+                    geom_valid.messages.append(
+                        f"For geometry variable '{geometry_var_name}' "
+                        "which defines the interior_ring attribute, "
+                        "the part_node_count attribute must also be present",
+                    )
+                # IMPLEMENTATION CONFORMANCE 7.5 REQUIRED 20/20
+                elif (
+                    part_node_count.dimensions != interior_ring.dimensions
+                    or not len(part_node_count) == 1
+                ):
+                    geom_valid.messages.append(
+                        f"part_node_count variable {part_node_count.name} "
+                        "must have the same single dimension as interior ring "
+                        f"variable {interior_ring.name}",
+                    )
+                else:
+                    geom_valid.score += 1
 
             if geometry_type == "point":
                 geometry = PointGeometry(node_coord_vars, node_count)


Test patch:
diff --git a/compliance_checker/tests/test_cf.py b/compliance_checker/tests/test_cf.py
index 29a2c2b..0aadfd5 100644
--- a/compliance_checker/tests/test_cf.py
+++ b/compliance_checker/tests/test_cf.py
@@ -2946,6 +2946,38 @@ class TestCF1_8(BaseTestCase):
         # There should be messages regarding improper polygon order
         assert results[0].value[0] < results[0].value[1]
         assert results[0].msgs
+        # TEST CONFORMANCE 7.5 REQUIRED 18/20
+        dataset.variables["geometry_container"] = MockVariable(
+            dataset.variables["geometry_container"],
+        )
+        del dataset.variables["geometry_container"].part_node_count
+        results = self.cf.check_geometry(dataset)
+        assert (
+            "For geometry variable 'geometry_container' "
+            "which defines the interior_ring attribute, "
+            "the part_node_count attribute must also be present" in results[0].msgs
+        )
+        dataset.close()
+        # TEST CONFORMANCE 7.5 REQUIRED 20/20
+        # part_node_count and interior_ring variables should have the same dimensions
+        # need diskless, no persist to make modifications to file without writing to file
+        # to file upon close
+        dataset = Dataset(
+            STATIC_FILES["polygon_geometry"],
+            "a",
+            diskless=True,
+            persist=False,
+        )
+        # we switch to another variable here to avoid name clashes
+        dataset.variables["geometry_container"].part_node_count = "part_node_count_bad"
+        dataset.createVariable("part_node_count_bad", "i4", ("time",))
+        results = self.cf.check_geometry(dataset)
+        assert (
+            "part_node_count variable part_node_count_bad "
+            "must have the same single dimension as interior ring "
+            "variable interior_ring" in results[0].msgs
+        )
+        dataset.close()
 
     def test_bad_lsid(self):
         """