perf(map): accelerate large spatial metrics
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:
Codex
2026-07-21 23:22:22 +02:00
parent 61731d9b0f
commit 17d0d85778
+20 -9
View File
@@ -11,7 +11,7 @@ from geoalchemy2.shape import to_shape
from shapely.geometry import box, mapping, shape from shapely.geometry import box, mapping, shape
from shapely.ops import transform as transform_geometry from shapely.ops import transform as transform_geometry
from shapely.validation import make_valid from shapely.validation import make_valid
from sqlalchemy import Float, String, cast, func from sqlalchemy import Float, String, case, cast, func
from app.core.errors import AppError from app.core.errors import AppError
from app.models import Dataset, VectorFeature from app.models import Dataset, VectorFeature
@@ -778,12 +778,18 @@ class VectorFeatureService:
) )
if method == "intersection_area": if method == "intersection_area":
measured_geometry = ( source_area = func.ST_Area(func.ST_Transform(VectorFeature.geometry, 31370))
VectorFeature.geometry if full_dataset_area:
if full_dataset_area area_expression = source_area
else func.ST_Intersection(VectorFeature.geometry, selection_shape) else:
) covered_by_selection = func.ST_CoveredBy(VectorFeature.geometry, selection_shape)
area_expression = func.ST_Area(func.ST_Transform(measured_geometry, 31370)) intersection_area = func.ST_Area(
func.ST_Transform(func.ST_Intersection(VectorFeature.geometry, selection_shape), 31370)
)
area_expression = case(
(covered_by_selection, source_area),
else_=intersection_area,
)
area_m2 = db.query(func.coalesce(func.sum(area_expression), 0.0)).filter(*metric_filter).scalar() area_m2 = db.query(func.coalesce(func.sum(area_expression), 0.0)).filter(*metric_filter).scalar()
divisor = 10_000.0 if unit == "ha" else 1.0 divisor = 10_000.0 if unit == "ha" else 1.0
metric_value = float(area_m2 or 0.0) / divisor metric_value = float(area_m2 or 0.0) / divisor
@@ -808,12 +814,17 @@ class VectorFeatureService:
) )
numeric_value = cast(VectorFeature.properties_json.op("->>")(property_name), Float) numeric_value = cast(VectorFeature.properties_json.op("->>")(property_name), Float)
value_expression = numeric_value value_expression = numeric_value
covered_by_selection = None
if method == "area_weighted_sum" and not full_dataset_area: if method == "area_weighted_sum" and not full_dataset_area:
source_area = func.ST_Area(func.ST_Transform(VectorFeature.geometry, 31370)) source_area = func.ST_Area(func.ST_Transform(VectorFeature.geometry, 31370))
intersection_area = func.ST_Area( intersection_area = func.ST_Area(
func.ST_Transform(func.ST_Intersection(VectorFeature.geometry, selection_shape), 31370) func.ST_Transform(func.ST_Intersection(VectorFeature.geometry, selection_shape), 31370)
) )
coverage_ratio = intersection_area / func.nullif(source_area, 0.0) covered_by_selection = func.ST_CoveredBy(VectorFeature.geometry, selection_shape)
coverage_ratio = case(
(covered_by_selection, 1.0),
else_=intersection_area / func.nullif(source_area, 0.0),
)
value_expression = numeric_value * coverage_ratio value_expression = numeric_value * coverage_ratio
aggregate_function = { aggregate_function = {
"mean": func.avg, "mean": func.avg,
@@ -831,7 +842,7 @@ class VectorFeatureService:
partial_feature_count = ( partial_feature_count = (
db.query(func.count(VectorFeature.id)) db.query(func.count(VectorFeature.id))
.filter(*metric_filter) .filter(*metric_filter)
.filter(coverage_ratio < 0.999999) .filter(~covered_by_selection)
.scalar() .scalar()
) )
is_estimate = bool(config.get("is_estimate", False)) or bool(partial_feature_count) is_estimate = bool(config.get("is_estimate", False)) or bool(partial_feature_count)