Pr/aoi builder - #169
Open
sdrichards-bc wants to merge 50 commits into
Open
Pr/aoi builder#169sdrichards-bc wants to merge 50 commits into
sdrichards-bc wants to merge 50 commits into
Conversation
… Return AIOBuildResult to caller.
…al attributes for upstream use.
…saligned, check meters units on gdf, improve polygon acquisition, and improve error handling.
…sts for sub modules.
… decoupling of modules during tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AOI: validate requests, harden normalization, and return structured build results
Summary
This PR strengthens the AOI builder's existing normalization, part-building, inspection, and validation workflow. It converts a raw
GeoDataFrameinto a normalizedAreaOfInterestand returns the AOI together with its validation findings and normalization audit report.The main caller-facing change is the move from
from_gdf(...) -> AreaOfInteresttobuild_from_request(...) -> AOIBuildResult. A completed build can contain validation errors; downstream callers must explicitly decide whether those errors prevent processing.Release highlights:
geometrycolumn.Motivation
The previous implementation already supported geometry repair, reprojection, three dissolve policies, overlap checks, singlepart AOIs, footprint and summed-part areas, and baseline validation. This release makes those capabilities more reliable and their contracts more explicit.
geometrycolumn; collection extraction examined only immediate members.dropna=Falseto retain null groups.crs_epsg=Noneand adds CRS text, counts, area ratio, vertices, and Z/M flags.AreaOfInterest; validation errors raised by default throughraise_errors=True.AOIBuildResultfor completed builds; callers evaluate validation findings.AOIError, stage-specific errors, chainedAOIBuildError, and build summaries.Key changes
Request validation
AOIRequestnow validates input types before normalization. It:by_fieldsand rejects non-empty fields for the other modes;allow_overlapsto be a boolean;target_crs_obj,target_epsg, andis_projected.The defaults remain
EPSG:3005,full_union, no dissolve fields, andallow_overlaps=False.AOIBuildRequestcombinesspecandraw_gdfand rejectsNonefor either field. It does not perform complete runtime type validation; the normalizer checks whether the raw data is a usable GeoDataFrame.Normalization
The normalizer checks raw input, cleans geometry, conforms CRS, applies policy, and checks the final geometry contract. Important changes include:
geometrywhen needed.geometryprevents the active-column rename.make_valid, and recursively retain polygon components from nested collections.The three policies already existed. Their current output contracts are:
full_unionby_fieldspreserve_featuresOverlap checks now use the spatial index to identify candidate pairs, then test positive intersection area. Shared edges and corners do not count as overlaps. Normalization uses the helper's default area tolerance of zero; no request-level tolerance setting has been added.
If overlaps remain and
allow_overlaps=False, normalization fails and the builder raisesAOIBuildError. This prohibition already existed; the error hierarchy and reporting are improved. Allowing overlaps permits them to remain but does not force a dissolve policy to preserve them.Normalization reporting
AOINormalizationReportBuilderowns mutable reporting state during normalization. Its finalAOINormalizationReportis frozen and records:The reporter checks that required values have been populated. The old normalizer did not populate
policy_applied,policy_input_feature_count, orpolicy_output_feature_countwhen constructing the final report; those values are now recorded.The
polygon_extract_*_feature_countnames describe component counts, not input rows. Also,null_or_empty_removed_countincludes rows removed after polygon extraction leaves no usable geometry. These counts should not be added together as if each represented a separate set of source rows.AOI parts
AOIPartBuildervalidates normalized input, explodes multipart geometry, and verifies that every resulting part is a valid single polygon. It constructs parts throughAOIPart.from_gdf, preserving CRS and the attributes retained by the selected dissolve policy.Part IDs now use zero-padded indexes, such as
aoi_001_part_0001. They follow normalized/exploded row order; they are not persistent geometry identifiers and are not guaranteed to remain attached to the same geometry if input or dissolve ordering changes.Each part adds
vertex_count,has_z, andhas_m, plusgeometryandcrsconvenience properties. Vertex counts include exterior and interior rings, including each ring's repeated closing coordinate.AOI inspection
AOIInspectornow records:overlay_area_hais renamed toparts_area_ha; the underlying distinction from unique footprint area already existed. When overlapping features are preserved, parts area can exceed footprint area. The new ratio makes that difference easier to inspect.A valid custom metric CRS may return
crs_epsg=None. Consumers should use the CRS object orcrs_stringwhere possible and enforce an EPSG requirement only at a boundary that needs it.Structured validation
AOIValidationResult.is_validis derived from its issues instead of being stored independently. Both validation and build results exposehas_errors,has_warnings,errors,warnings, andinfos.ValidationIssuenormalizes severity to lowercase, codes to uppercase, and surrounding message whitespace. It rejects unsupported severity and blank codes or messages.The current validator retains these checks:
OVERLAPS_PRESENTNULL_OR_NON_POLYGONS_REMOVEDINVALID_GEOMETRYZERO_AREAPART_COUNT_MISMATCHZERO_AREA_OR_SLIVER_PARTLARGE_PARTThe area thresholds and their error severity are unchanged. They report findings; they do not remove small parts or split large parts. Allowed overlaps are recorded in the normalization report without generating an overlap warning solely because they are allowed.
The latest validator no longer emits
NO_GDF,NO_PROPERTIES,NO_PARTS, orNO_NORMALIZATION_REPORT. The normal build workflow establishes these inputs through earlier stages; direct calls to the validator must supply them. The uploaded README still lists these four checks and should be aligned with the implementation.Build-result contract
The builder returns:
Here, “success-only” means that construction completed. It does not mean that all validation rules passed.
AOIRequestErrorduring request construction.AOIBuildError; no result is returned.AOIBuildResultwithhas_errors=Trueandis_valid=False.AOIBuildResultwithhas_warnings=Trueandis_valid=True.AOIBuildResultwithis_valid=True.Malformed untyped calls outside the declared API are not guaranteed to become
AOIBuildError: request access and final result assembly occur outside_run_stage().Exceptions and logging
AOI domain exceptions now inherit from
AOIError. The hierarchy distinguishes request configuration, spatial data/CRS/geometry failures, stage failures, and top-level build failures.AOIBuilder._run_stage()recordsstageandaoi_idonAOIBuildErrorand preserves the original exception chain. Stage names arenormalization,part_building,inspection, andvalidation.root_cause()follows explicit__cause__links.Expected AOI failures receive concise error logs, with root tracebacks at debug level. Unexpected exceptions raised within a stage are logged with their traceback and wrapped. Build start/completion and validation summaries are logged by the builder; normalization, part, and inspection detail is available at debug level.
Constructor defaults now use explicit
is not Nonechecks. A supplied normalizer, inspector, validator, or part builder is retained even if it evaluates as false.Public API and migration
This is a breaking caller-facing change.
builder.from_gdf(spec, raw_gdf)builder.build_from_request(AOIBuildRequest(spec=spec, raw_gdf=raw_gdf))AreaOfInterestresult.aoi.raise_errors=True/Falseresult.has_errors; explicitly choose whether to stop.aoi.validationresult.validationaoi.normalization_reportresult.normalization_reportaoi.overlay_area_haorproperties.overlay_area_haaoi.parts_area_haorproperties.parts_area_haAOIValidationResult(is_valid=..., issues=...)AOIValidationResult(issues=...); validity is computed.part_builder.build_parts(aoi_id, gdf)part_builder.build_parts(aoi_id=aoi_id, gdf=gdf)_part_1_part_0001; update snapshots and references.crs_epsg=Nonefor custom metric CRSs.by_fieldsgeometry.dataclasses.replace()for test variations.AOIGeometryError/AOIGeometryTypeErrorSpatialGeometryErrorand the builder boundary.AOIPart/AOIPropertiesconstructionTo preserve the previous default behavior of stopping on validation errors, make that decision explicitly in the calling service:
AOIRequestErrorand fatalAOIBuildErrorpropagate to the application's error handler. The explicitAOIValidationErrorabove is raised by the calling service, not automatically by the new builder. A review/reporting workflow can instead retain the whole result and present its findings.Files changed
All eight original Python files changed. Three Python files and a README were added; no source files were removed.
aoi_builder.pyAOIBuildResult, and provides stage error/logging boundaries.models.pynormalizer.pyparts_builder.pyinspector.pyvalidator.pyspatial_validatorbut does not use it yet.exceptions.py__init__.pynormalization_reporter.pyutils.pyconstants.pyDEFAULT_CRS = "EPSG:3005"andDEFAULT_GEOM_FIELD = "geometry".README.mdTesting
The supplied AOI suite contains eight test modules with 136 test functions before parameter expansion. It covers component behavior, mocked builder orchestration, real AOI-stage workflows using generated input, and the shared geometry factories used by those tests.
Behaviors exercised
Verification before merge
AOIBuildResultcontract and changed AOI fields.Remaining coverage to confirm
The uploaded suite does not directly establish all items in the earlier checklist. Check for existing tests elsewhere in the repository before adding more:
("REGION", " REGION ").AOIBuildRequestrejection of missingspecorraw_gdf.ValidationIssueseverity/code normalization, rejection of unsupported/blank values, and complete validation-result filtering behavior.LARGE_PARTat the configured large-area limit; it does not cover the sliver rule or all boundary controls.by_fieldsoutput. Existing tests assert grouped geometry and keys, but do not directly assert that an extra non-group column is absent.geometry, and rejection of the active geometry column as a dissolve key.The supplied archive has no dedicated validator test module. Builder tests exercise actual large-part findings, and orchestration tests inject controlled findings to check the result contract. Those tests do not demonstrate complete coverage of every validator rule. If a separate validator suite exists, include it in the run and reference it here.
Reviewer focus
Pay particular attention to these compatibility and documentation details:
validator.pyimportsSpatialValidatorfromast_engine.core.validation.spatial_validationat runtime but only stores the supplied object. Standalone use of this archive requires that external module.Known limitations and follow-up work
The validator provides baseline findings only.
MAX_VERTICES = 10_000is declared but not enforced. The injectedSpatialValidatoris not invoked. Provincial/project footprint checks, expected-region or management-unit checks, boundary crossing, hole/donut checks, and configurable thresholds/severities remain future work.Frozen dataclasses do not make contained GeoDataFrames immutable.
AreaOfInterestis mutable, and part/build-result objects still contain mutable data. Downstream geometry edits can invalidate cached areas, bounds, counts, or validation findings; rebuild or recompute metadata when geometry changes.Release-note summary
AOI preparation now validates configuration before processing, requires a projected target CRS using metres, and provides clearer build diagnostics. Geometry normalization handles nested collections, standardizes the active geometry column, and records its changes in an immutable audit report. Grouped dissolves retain null groups and return only their grouping fields and geometry. AOI parts preserve CRS and retained attributes, and inspection adds area ratios, geometry-complexity metadata, and support for custom metric CRSs without EPSG codes.
Migration required: replace
from_gdf(...)withbuild_from_request(AOIBuildRequest(...)), read the AOI fromresult.aoi, and checkresult.has_errorsbefore processing. Validation and normalization reports now belong to the build result;overlay_area_hais renamed toparts_area_ha. Review grouped attributes, geometry-column assumptions, and part-ID formatting in downstream consumers.Spatial-context and maximum-vertex validation remain future work.
Check List
**Breaking change to existing tests outside of tests/unit/aoi. PR to resolve those issues and align with the updated aoi builder boundary.