@@ -2213,6 +2213,41 @@ def _camera_source_row(
22132213 }
22142214
22152215
2216+ def _camera_class_rows (
2217+ * ,
2218+ record : Mapping [str , object ],
2219+ camera_name : str ,
2220+ camera_zoom : object ,
2221+ input_report : str ,
2222+ ) -> list [dict [str , object ]]:
2223+ property_areas = record .get ("property_overlap_areas" )
2224+ if not isinstance (property_areas , Mapping ):
2225+ return []
2226+ class_areas = property_areas .get ("class" )
2227+ if not isinstance (class_areas , Mapping ):
2228+ return []
2229+ rows : list [dict [str , object ]] = []
2230+ for class_name , area_record in class_areas .items ():
2231+ if not isinstance (area_record , Mapping ):
2232+ continue
2233+ coverage = _float_value (area_record .get ("crop_coverage_ratio" ))
2234+ if coverage <= 0.0 :
2235+ continue
2236+ rows .append (
2237+ {
2238+ "input_report" : input_report ,
2239+ "camera" : camera_name ,
2240+ "camera_zoom" : _float_value (camera_zoom ),
2241+ "source_layer" : str (record .get ("source_layer" ) or MISSING_VALUE ),
2242+ "class" : str (class_name ),
2243+ "class_coverage" : _rounded_float (coverage ),
2244+ "overlap_feature_count" : _int_value (record .get ("overlap_feature_count" )),
2245+ "qgis_style_layer_coverage" : _format_style_layer_matches (record ),
2246+ }
2247+ )
2248+ return rows
2249+
2250+
22162251def _source_layer_total_row (total : _AggregateSourceLayerTotal ) -> dict [str , object ]:
22172252 return {
22182253 "source_layer" : total .source_layer ,
@@ -2247,6 +2282,7 @@ def _aggregate_one_source_crop_report(
22472282 source_totals : dict [str , _AggregateSourceLayerTotal ],
22482283 style_totals : dict [tuple [str , str ], _AggregateStyleLayerTotal ],
22492284 camera_rows : list [dict [str , object ]],
2285+ camera_class_rows : list [dict [str , object ]],
22502286 qgis_runtimes : set [str ],
22512287) -> str :
22522288 resolved_path = report_path .expanduser ().resolve ()
@@ -2277,6 +2313,14 @@ def _aggregate_one_source_crop_report(
22772313 input_report = input_report ,
22782314 )
22792315 )
2316+ camera_class_rows .extend (
2317+ _camera_class_rows (
2318+ record = record ,
2319+ camera_name = camera_name ,
2320+ camera_zoom = report .get ("camera_zoom" ),
2321+ input_report = input_report ,
2322+ )
2323+ )
22802324 return input_report
22812325
22822326
@@ -2317,6 +2361,21 @@ def _deduplicated_report_paths(report_paths: Sequence[Path]) -> list[Path]:
23172361 return unique_paths
23182362
23192363
2364+ def _sorted_camera_class_rows (rows : Sequence [Mapping [str , object ]]) -> list [dict [str , object ]]:
2365+ return [
2366+ dict (row )
2367+ for row in sorted (
2368+ rows ,
2369+ key = lambda row : (
2370+ - float (row .get ("class_coverage" ) or 0.0 ),
2371+ str (row .get ("camera" )),
2372+ str (row .get ("source_layer" )),
2373+ str (row .get ("class" )),
2374+ ),
2375+ )
2376+ ]
2377+
2378+
23202379def build_source_crop_overlap_aggregate_report (
23212380 report_paths : Sequence [Path ],
23222381 * ,
@@ -2328,13 +2387,15 @@ def build_source_crop_overlap_aggregate_report(
23282387 source_totals : dict [str , _AggregateSourceLayerTotal ] = {}
23292388 style_totals : dict [tuple [str , str ], _AggregateStyleLayerTotal ] = {}
23302389 camera_rows : list [dict [str , object ]] = []
2390+ camera_class_rows : list [dict [str , object ]] = []
23312391 qgis_runtimes : set [str ] = set ()
23322392 input_reports = [
23332393 _aggregate_one_source_crop_report (
23342394 report_path ,
23352395 source_totals = source_totals ,
23362396 style_totals = style_totals ,
23372397 camera_rows = camera_rows ,
2398+ camera_class_rows = camera_class_rows ,
23382399 qgis_runtimes = qgis_runtimes ,
23392400 )
23402401 for report_path in deduplicated_report_paths
@@ -2354,6 +2415,7 @@ def build_source_crop_overlap_aggregate_report(
23542415 str (row ["source_layer" ]),
23552416 ),
23562417 ),
2418+ "camera_class_rows" : _sorted_camera_class_rows (camera_class_rows ),
23572419 }
23582420
23592421
@@ -2420,6 +2482,20 @@ def _aggregate_camera_source_row(row: Mapping[str, object]) -> str:
24202482 )
24212483
24222484
2485+ def _aggregate_camera_class_row (row : Mapping [str , object ]) -> str :
2486+ return _markdown_table_row (
2487+ [
2488+ f"`{ row .get ('camera' )} `" ,
2489+ _format_camera_zoom (row .get ("camera_zoom" )),
2490+ f"`{ row .get ('source_layer' )} `" ,
2491+ row .get ("class" ) or "-" ,
2492+ _format_coverage (row .get ("class_coverage" )),
2493+ row .get ("overlap_feature_count" ),
2494+ row .get ("qgis_style_layer_coverage" ) or "-" ,
2495+ ]
2496+ )
2497+
2498+
24232499def _aggregate_source_read_labels (rows : Sequence [Mapping [str , object ]]) -> list [str ]:
24242500 return [
24252501 (
@@ -2465,10 +2541,23 @@ def _aggregate_zero_overlap_labels(rows: Sequence[Mapping[str, object]]) -> list
24652541 ][:5 ]
24662542
24672543
2544+ def _aggregate_camera_class_read_labels (rows : Sequence [Mapping [str , object ]]) -> list [str ]:
2545+ return [
2546+ (
2547+ f"{ row .get ('camera' ) or MISSING_VALUE } "
2548+ f"{ row .get ('source_layer' ) or MISSING_VALUE } /{ row .get ('class' ) or MISSING_VALUE } ="
2549+ f"{ float (row .get ('class_coverage' ) or 0.0 ):.3f} "
2550+ )
2551+ for row in rows
2552+ if float (row .get ("class_coverage" ) or 0.0 ) > 0.0
2553+ ][:5 ]
2554+
2555+
24682556def _aggregate_read_lines (
24692557 * ,
24702558 source_rows : Sequence [Mapping [str , object ]],
24712559 style_rows : Sequence [Mapping [str , object ]],
2560+ camera_class_rows : Sequence [Mapping [str , object ]],
24722561) -> list [str ]:
24732562 return [
24742563 "" ,
@@ -2477,6 +2566,7 @@ def _aggregate_read_lines(
24772566 f"- Top source-layer bbox coverage sums: { _joined_read_labels (_aggregate_source_read_labels (source_rows ))} ." ,
24782567 f"- Top QGIS style-layer bbox coverage sums: { _joined_read_labels (_aggregate_style_read_labels (style_rows ))} ." ,
24792568 f"- Top source-layer class coverage sums: { _joined_read_labels (_aggregate_class_read_labels (source_rows ))} ." ,
2569+ f"- Top camera/class coverage candidates: { _joined_read_labels (_aggregate_camera_class_read_labels (camera_class_rows ))} ." ,
24802570 (
24812571 "- Source layers with zero overlap wherever requested: "
24822572 f"{ _joined_read_labels (_aggregate_zero_overlap_labels (source_rows ))} ."
@@ -2491,6 +2581,7 @@ def render_aggregate_markdown_summary(report: Mapping[str, object]) -> str:
24912581 source_rows = _list_of_mappings (report .get ("source_layer_rows" ))
24922582 style_rows = _list_of_mappings (report .get ("style_layer_rows" ))
24932583 camera_rows = _list_of_mappings (report .get ("camera_source_rows" ))
2584+ camera_class_rows = _list_of_mappings (report .get ("camera_class_rows" ))
24942585 lines = [
24952586 "# Mapbox Outdoors source/crop overlap aggregate" ,
24962587 "" ,
@@ -2503,7 +2594,13 @@ def render_aggregate_markdown_summary(report: Mapping[str, object]) -> str:
25032594 lines .extend (f"- `{ path } `" for path in input_reports [:20 ])
25042595 if len (input_reports ) > 20 :
25052596 lines .append (f"- ... { len (input_reports ) - 20 } more" )
2506- lines .extend (_aggregate_read_lines (source_rows = source_rows , style_rows = style_rows ))
2597+ lines .extend (
2598+ _aggregate_read_lines (
2599+ source_rows = source_rows ,
2600+ style_rows = style_rows ,
2601+ camera_class_rows = camera_class_rows ,
2602+ )
2603+ )
25072604 lines .extend ([
25082605 "" ,
25092606 "## Source layer totals" ,
@@ -2528,6 +2625,14 @@ def render_aggregate_markdown_summary(report: Mapping[str, object]) -> str:
25282625 "| --- | ---: | --- | ---: | ---: | --- | --- | --- |" ,
25292626 ])
25302627 lines .extend (_aggregate_camera_source_row (row ) for row in camera_rows ) if camera_rows else lines .append ("| _none_ | | | 0 | 0 | | | |" )
2628+ lines .extend ([
2629+ "" ,
2630+ "## Camera/class probe candidates" ,
2631+ "" ,
2632+ "| Camera | Zoom | Source layer | Class | Class coverage | Overlap features | QGIS style-layer coverage |" ,
2633+ "| --- | ---: | --- | --- | ---: | ---: | --- |" ,
2634+ ])
2635+ lines .extend (_aggregate_camera_class_row (row ) for row in camera_class_rows ) if camera_class_rows else lines .append ("| _none_ | | | | 0 | 0 | |" )
25312636 lines .append ("" )
25322637 return "\n " .join (lines )
25332638
0 commit comments