Add fail-closed source class and SAM fallback filters
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Jens
2026-07-30 04:24:35 +02:00
parent 29378bba42
commit 08422ca50c
6 changed files with 74 additions and 4 deletions
@@ -148,6 +148,7 @@ def normalize(
reference_observed_at: str | None,
imagery_valid_to: str | None = None,
merge_touching_roofs: bool = False,
allowed_source_classes: set[str] | None = None,
) -> tuple[dict[str, Any], dict[str, Any]]:
if source_name not in SUPPORTED_SOURCES:
raise SystemExit(f"Unsupported governed building source: {source_name}")
@@ -184,6 +185,7 @@ def normalize(
"reason": None,
"geometry_repaired": False,
}
source_class = decision["source_class"]
source_creation_at = _source_creation_at(source_name, properties)
decision["source_creation_at"] = source_creation_at.isoformat() if source_creation_at else None
try:
@@ -199,6 +201,8 @@ def normalize(
geometry = _polygonal(geometry)
if geometry is None or geometry.is_empty or not geometry.is_valid:
decision["reason"] = "invalid_geometry_unrepairable"
elif allowed_source_classes is not None and source_class not in allowed_source_classes:
decision["reason"] = "source_class_not_allowed"
elif (reason := _semantic_exclusion(properties)) is not None:
decision["reason"] = reason
elif imagery_cutoff and source_creation_at and source_creation_at > imagery_cutoff:
@@ -276,6 +280,7 @@ def normalize(
"accepted_source_feature_count": len(accepted),
"accepted_feature_count": len(normalized_features),
"merge_touching_roofs": merge_touching_roofs,
"allowed_source_classes": sorted(allowed_source_classes) if allowed_source_classes is not None else None,
"decision_counts": dict(sorted(counts.items())),
"decisions": decisions,
}
@@ -294,6 +299,12 @@ def main() -> int:
parser.add_argument("--reference-observed-at")
parser.add_argument("--imagery-valid-to")
parser.add_argument("--merge-touching-roofs", action="store_true")
parser.add_argument(
"--source-class",
action="append",
dest="source_classes",
help="Repeatable provider-native class allowlist. No cross-provider semantic mapping is inferred.",
)
args = parser.parse_args()
normalized, audit = normalize(
reference_path=args.reference,
@@ -304,6 +315,7 @@ def main() -> int:
reference_observed_at=args.reference_observed_at,
imagery_valid_to=args.imagery_valid_to,
merge_touching_roofs=args.merge_touching_roofs,
allowed_source_classes=set(args.source_classes) if args.source_classes else None,
)
args.output_reference.parent.mkdir(parents=True, exist_ok=True)
args.output_audit.parent.mkdir(parents=True, exist_ok=True)