* feat(site-finder): P1 geometry suitability score (#45)
Backend (parcels.py):
- _polygon_suitability() — Shapely metrics on parcel WGS84 polygon:
area_ha/m2 (projected via cos(lat) к UTM-like meters), perimeter_m,
aspect_ratio (long/short side of MABR), convex_hull_ratio (area / hull area),
min_inscribed_rect_dim_m.
- Composite suitability score 0..1:
base = area_subscore (0 при <0.2 ha, linear → 1 при ≥0.5 ha)
−0.3 если aspect_ratio > 5 (вытянутый)
−0.3 если convex_hull_ratio < 0.65 (изрезанный)
−0.5 если min_inscribed_rect_dim_m < 30 (узкий)
- Label: микро / подходящий / сложная форма / слабо подходит
- Recommendation: "для МКД 16+ нужно >0.3 ha + минимум 40м"
- В analyze response → geometry_suitability.
Frontend:
- Новый GeometrySuitabilityBlock.tsx с color-coded badge + метрики grid +
penalties + recommendation
- Добавлен в LandTab (выше "Зонирование (ПЗЗ)")
- TS типы расширены: GeometrySuitability
Closes#45.
* fix(site-finder): address PR #89 auto-review minor feedback
Backend (parcels.py):
1. Фактическая ошибка фикс: recommendation теперь различает "строительный
минимум 30м" (physical penalty trigger) и "комфорт МКД 12-16эт 40м"
(recommendation level). Пользователю чётко видны 2 порога.
2. Lazy imports → module-level: `from shapely import wkt as _shp_wkt`,
`from shapely.geometry import Polygon`.
3. MABR inner exception теперь логирует: `logger.debug("MABR computation
failed, falling back to sqrt(area): %s", mabr_err)`.
4. Magic numbers вынесены в константы: _GEOM_MIN_AREA_HA, _GEOM_AREA_SCORE_FULL_HA,
_GEOM_ASPECT_PENALTY_THRESHOLD, _GEOM_CONVEX_PENALTY_THRESHOLD,
_GEOM_MIN_WIDTH_PHYSICAL_M, _GEOM_MIN_WIDTH_COMFORT_M, _GEOM_LABEL_MICRO_HA,
_GEOM_LABEL_GOOD, _GEOM_LABEL_MEDIUM, плюс penalty константы.
5. Label "микро" теперь комбинируется с penalties: "микро, узкий" — UI видит
обе проблемы. Pure "микро" остаётся когда нет penalty.
Frontend (GeometrySuitabilityBlock.tsx + types):
6. Заменил emoji ⚠ (U+26A0) на текстовый <strong>"Проблемы формы:"</strong> —
стабильнее в WeasyPrint PDF-экспорте и cross-platform.
- TS типы: GeometrySuitabilityBaseLabel экспортирован, label расширен до
`string` для допуска combo-labels; helper colorForLabel() парсит base часть.
Per auto-review on f4e7491.
* fix(site-finder): P1 #45 — extract 0.3 ha to _GEOM_AREA_COMFORT_HA
Auto-review нашёл: recommendation упоминала "от 0.3 га" но в constants block
не было 0.3 — magic literal в строке. Тот же класс проблемы что 30/40м inconsistency
из прошлого review, только в другом поле.
Fix: новая константа `_GEOM_AREA_COMFORT_HA = 0.3` с комментарием
"рекомендуемая комфортная площадь МКД (recommendation)". Размещена между
_GEOM_MIN_AREA_HA (физический минимум) и _GEOM_AREA_SCORE_FULL_HA (premium) —
третий semantic threshold. Recommendation теперь f-string использует константу.
Per auto-review on ae5e8de.
---------
Co-authored-by: lekss361 <claudestars@proton.me>