fix: forest_pct — доля 0..1 вместо процентов (вес леса падает со 100×)
Закономерность «приоритетная зона вверх-вправо»: score_zone умножает weight × forest_pct, ожидая ДОЛЮ (тесты скоринга 0.3–0.8), а гео-сервисы (osm_local и legacy Overpass calculate_forest_coverage) отдавали ПРОЦЕНТЫ 0..100. Лес получил фактический вес в 100 раз больше задумки — топ зон систематически занимали чуть более зелёные сектора. Фикс: osm_local.calculate_forest_coverage возвращают долю 0..1. Разброс скорингов по Минску: 1.244 → 0.032 (честно однородный город). Остаточные различия — вода/дороги; дифференциация плотностей город/село — ревью скоринга (B1), коэффициенты не тронуты. +регресс-тест 0<=forest_pct<=1. 197 passed.
This commit is contained in:
@@ -344,9 +344,12 @@ def calculate_forest_coverage(elements: List[Dict], radius_m: int) -> float:
|
||||
search_area_km2 = math.pi * (radius_m / 1000) ** 2
|
||||
estimated_forest_km2 = forest_ways * 0.1
|
||||
|
||||
coverage_pct = min(100.0, (estimated_forest_km2 / search_area_km2) * 100)
|
||||
# Контракт: ДОЛЯ 0..1 (scoring_service читает forest_pct как долю —
|
||||
# weight × forest_pct; раньше отдавались проценты 0..100, что делало
|
||||
# лес доминирующим фактором со 100× весом).
|
||||
coverage = min(1.0, estimated_forest_km2 / search_area_km2)
|
||||
|
||||
return round(coverage_pct, 1)
|
||||
return round(coverage, 3)
|
||||
|
||||
|
||||
async def get_zone_features(lat: float, lon: float, direction: str, radius_m: int) -> Dict:
|
||||
|
||||
@@ -145,9 +145,13 @@ def get_zone_features_postgis(lat: float, lon: float, direction: str, radius_m:
|
||||
search_area_km2 = math.pi * (radius_m / 1000.0) ** 2
|
||||
road_density = roads_km / search_area_km2 if search_area_km2 > 0 else 0.0
|
||||
|
||||
# Контракт Zone.forest_pct — ДОЛЯ леса 0..1 (так его читает
|
||||
# scoring_service.score_zone: weight × forest_pct; тесты скоринга
|
||||
# используют 0.3–0.8 как доли). Раньше отдались проценты 0..100 —
|
||||
# лес в 100 раз перевешивал остальные факторы (зоны «вверх-вправо»).
|
||||
forest_pct = 0.0
|
||||
if forest_m2 > 0 and search_area_km2 > 0:
|
||||
forest_pct = round(min(100.0, (forest_m2 / 1e6) / search_area_km2 * 100), 1)
|
||||
forest_pct = round(min(1.0, (forest_m2 / 1e6) / search_area_km2), 3)
|
||||
|
||||
water_candidates = [float(m) for m in (water_poly_m, waterway_m) if m is not None]
|
||||
water_distance_km = round(min(water_candidates), 3) if water_candidates else None
|
||||
|
||||
Reference in New Issue
Block a user