perf: optimize trusted full-area analysis
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-14 22:57:09 +02:00
parent c4392c17fd
commit 0381fdd636
10 changed files with 164 additions and 15 deletions
+4
View File
@@ -248,10 +248,13 @@ def select_vector_features(
"bbox": payload.bbox.model_dump(),
"limit": payload.limit,
}
full_dataset_area = False
if selection_area is not None:
full_dataset_area = VectorFeatureService.can_use_full_area_fast_path(dataset, selection_area.id)
selection_kwargs.update(
selection_geometry=selection_area.geometry,
selection_area_id=selection_area.id,
full_dataset_area=full_dataset_area,
)
result = VectorFeatureService.select_features_by_bbox(db, **selection_kwargs)
if isinstance(dataset.source_metadata, dict) and dataset.source_metadata.get("selection_aggregation"):
@@ -262,6 +265,7 @@ def select_vector_features(
}
if selection_area is not None:
summary_kwargs["selection_geometry"] = selection_area.geometry
summary_kwargs["full_dataset_area"] = full_dataset_area
result["summary"] = VectorFeatureService.summarize_features_by_bbox(db, **summary_kwargs)
return envelope(VectorSelectionResponse(**result).model_dump(exclude_none=True))
+45 -15
View File
@@ -18,7 +18,25 @@ from app.core.errors import AppError
from app.models import Dataset, VectorFeature
FULL_AREA_CLIPPED_OPERATOR_TOOLS = {
"provision_mol_population_history.py",
"provision_official_landuse_timeseries.py",
"provision_regional_grb_buildings.py",
"provision_regional_grb_context.py",
}
class VectorFeatureService:
@staticmethod
def can_use_full_area_fast_path(dataset: Dataset, selection_area_id: UUID | None) -> bool:
if selection_area_id is None or dataset.area_id != selection_area_id:
return False
source_metadata = dataset.source_metadata if isinstance(dataset.source_metadata, dict) else {}
if source_metadata.get("geometry_clipped_to_area") is True:
return True
provenance = dataset.provenance_metadata if isinstance(dataset.provenance_metadata, dict) else {}
return provenance.get("operator_tool") in FULL_AREA_CLIPPED_OPERATOR_TOOLS
@staticmethod
def _feature_row(dataset_id: UUID, feature: dict[str, Any], index: int, feature_class: str | None) -> VectorFeature | None:
geometry_payload = feature.get("geometry")
@@ -126,6 +144,7 @@ class VectorFeatureService:
dataset: Dataset | None = None,
selection_geometry: Any | None = None,
selection_area_id: UUID | None = None,
full_dataset_area: bool = False,
) -> dict[str, Any]:
normalized_bbox = VectorFeatureService._normalize_selection_bbox(bbox)
safe_limit = max(1, min(int(limit), 1000))
@@ -139,11 +158,9 @@ class VectorFeatureService:
4326,
)
query = (
db.query(VectorFeature)
.filter(VectorFeature.dataset_id == dataset_id)
.filter(ST_Intersects(VectorFeature.geometry, selection_shape))
)
query = db.query(VectorFeature).filter(VectorFeature.dataset_id == dataset_id)
if not full_dataset_area:
query = query.filter(ST_Intersects(VectorFeature.geometry, selection_shape))
if hasattr(query, "count"):
total_feature_count = int(query.count())
else: # Lightweight unit-test sessions do not always implement Query.count().
@@ -165,6 +182,7 @@ class VectorFeatureService:
bbox=normalized_bbox,
total_feature_count=total_feature_count,
selection_geometry=selection_geometry,
full_dataset_area=full_dataset_area,
)
result = {
@@ -191,6 +209,7 @@ class VectorFeatureService:
bbox: dict[str, Any],
total_feature_count: int | None = None,
selection_geometry: Any | None = None,
full_dataset_area: bool = False,
) -> dict[str, Any]:
normalized_bbox = VectorFeatureService._normalize_selection_bbox(bbox)
selection_shape = selection_geometry
@@ -202,10 +221,9 @@ class VectorFeatureService:
normalized_bbox["max_y"],
4326,
)
selection_filter = (
VectorFeature.dataset_id == dataset.id,
ST_Intersects(VectorFeature.geometry, selection_shape),
)
selection_filter = (VectorFeature.dataset_id == dataset.id,)
if not full_dataset_area:
selection_filter += (ST_Intersects(VectorFeature.geometry, selection_shape),)
feature_count = total_feature_count
if feature_count is None:
feature_count = int(db.query(func.count(VectorFeature.id)).filter(*selection_filter).scalar() or 0)
@@ -222,14 +240,22 @@ class VectorFeatureService:
metric_value = float(feature_count)
if method == "intersection_area":
intersection = func.ST_Intersection(VectorFeature.geometry, selection_shape)
area_expression = func.ST_Area(func.ST_Transform(intersection, 31370))
measured_geometry = (
VectorFeature.geometry
if full_dataset_area
else func.ST_Intersection(VectorFeature.geometry, selection_shape)
)
area_expression = func.ST_Area(func.ST_Transform(measured_geometry, 31370))
area_m2 = db.query(func.coalesce(func.sum(area_expression), 0.0)).filter(*selection_filter).scalar()
divisor = 10_000.0 if unit == "ha" else 1.0
metric_value = float(area_m2 or 0.0) / divisor
elif method == "intersection_length":
intersection = func.ST_Intersection(VectorFeature.geometry, selection_shape)
length_expression = func.ST_Length(func.ST_Transform(intersection, 31370))
measured_geometry = (
VectorFeature.geometry
if full_dataset_area
else func.ST_Intersection(VectorFeature.geometry, selection_shape)
)
length_expression = func.ST_Length(func.ST_Transform(measured_geometry, 31370))
length_m = db.query(func.coalesce(func.sum(length_expression), 0.0)).filter(*selection_filter).scalar()
divisor = 1_000.0 if unit == "km" else 1.0
metric_value = float(length_m or 0.0) / divisor
@@ -244,7 +270,7 @@ class VectorFeatureService:
)
numeric_value = cast(VectorFeature.properties_json.op("->>")(property_name), Float)
value_expression = numeric_value
if method == "area_weighted_sum":
if method == "area_weighted_sum" and not full_dataset_area:
source_area = func.ST_Area(func.ST_Transform(VectorFeature.geometry, 31370))
intersection_area = func.ST_Area(
func.ST_Transform(func.ST_Intersection(VectorFeature.geometry, selection_shape), 31370)
@@ -258,7 +284,7 @@ class VectorFeatureService:
.scalar()
)
metric_value = float(aggregate_value or 0.0)
if method == "area_weighted_sum":
if method == "area_weighted_sum" and not full_dataset_area:
partial_feature_count = (
db.query(func.count(VectorFeature.id))
.filter(*selection_filter)
@@ -268,6 +294,10 @@ class VectorFeatureService:
is_estimate = bool(partial_feature_count)
if not is_estimate and config.get("warning_only_when_estimate", True):
warning = None
elif method == "area_weighted_sum":
is_estimate = False
if config.get("warning_only_when_estimate", True):
warning = None
elif method != "feature_count":
raise AppError(
code="INVALID_SELECTION_AGGREGATION",