perf(map): accelerate large spatial metrics
This commit is contained in:
@@ -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)
|
||||||
|
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_expression = func.ST_Area(func.ST_Transform(measured_geometry, 31370))
|
|
||||||
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user