fix(concept): §7 massing-сцена осознаёт весь MultiPolygon (соседи уезжали до 1.6км) (#2180) #2463

Merged
bot-backend merged 1 commit from fix/massing-multipolygon-whole-parcel into main 2026-07-07 08:10:28 +00:00
Collaborator

Симптом

На §7 «Размещение застройки» (3D «Объём» / 2D «План») соседние здания рендерились грубо смещёнными относительно участка и OSM-подложки — для многочастных кадастровых участков (скриншот-репорт от Anton).

Root cause (доказан прод-данными)

extractPolygon (concept-api.ts) схлопывал GeoJSON MultiPolygon до первой части (coordinates[0]). Massing3DScene.buildModel брал по ней контур участка + начало проекции lon0/lat0 + bbox OSM-тайла. А бэкенд /neighbor-buildings анкорит соседей к ЦЕЛОЙ геометрии (ST_DWithin вокруг всего cad_parcels_geom.geom). Для многочастного участка, где part[0] смещён от центроида целого, соседи проецировались вокруг «чужого» центра.

Масштаб (прод): 41 234 MultiPolygon-участка, 437 многочастные, 163 со сдвигом part[0]↔целое >100м (max 1611м) — совпадает с наблюдаемым.

Исключено с числами: Mercator-дрейф <0.1м, СК 4326, единый проектор корпуса/соседей, источник geom_geojson без контаминации.

Fix

  • geo-local.ts: ringsBounds/ringsCentroid/polygonOuterRings; старые ringBounds/ringCentroid делегируют к plural-версиям (single-ring байт-в-байт идентично → одночастные участки не затронуты).
  • Massing3DScene.buildModel: origin ← ringsCentroid, groundBounds ← ringsBounds (union всех частей), контур per-part, камера охватывает все части.
  • extractPolygon: MultiPolygon-ветка берёт наибольшую по площади часть (было coordinates[0]) → корпус строится на главной части, не осколке. Новый extractParcelGeometry отдаёт сцене всю геометрию.
  • ConceptResultMap (2D): geometryBounds фитит по всему экстенту.
  • MassingScene (ptica) area-based → не затронута.

Test plan

  • 264 vitest зелёные (+ geo-local multi-part кейсы: centroid между частями, bounds-union; concept-api: largest-part selection на first≠largest)
  • tsc --noEmit чисто, next lint, prettier
  • code-reviewer: APPROVE (single-Polygon регрессия строго исключена — делегация bit-identical)

Post-deploy verify (rule #7)

Визуальный смоук на известном многочастном участке — соседи теперь совмещены с контуром и OSM.

Refs #2180

## Симптом На §7 «Размещение застройки» (3D «Объём» / 2D «План») соседние здания рендерились грубо смещёнными относительно участка и OSM-подложки — для **многочастных** кадастровых участков (скриншот-репорт от Anton). ## Root cause (доказан прод-данными) `extractPolygon` ([concept-api.ts](frontend/src/lib/concept-api.ts)) схлопывал GeoJSON `MultiPolygon` до **первой части** (`coordinates[0]`). `Massing3DScene.buildModel` брал по ней **контур участка + начало проекции lon0/lat0 + bbox OSM-тайла**. А бэкенд `/neighbor-buildings` анкорит соседей к **ЦЕЛОЙ** геометрии (`ST_DWithin` вокруг всего `cad_parcels_geom.geom`). Для многочастного участка, где part[0] смещён от центроида целого, соседи проецировались вокруг «чужого» центра. **Масштаб (прод):** 41 234 MultiPolygon-участка, 437 многочастные, **163 со сдвигом part[0]↔целое >100м (max 1611м)** — совпадает с наблюдаемым. Исключено с числами: Mercator-дрейф <0.1м, СК 4326, единый проектор корпуса/соседей, источник `geom_geojson` без контаминации. ## Fix - `geo-local.ts`: `ringsBounds`/`ringsCentroid`/`polygonOuterRings`; старые `ringBounds`/`ringCentroid` делегируют к plural-версиям (single-ring **байт-в-байт** идентично → одночастные участки не затронуты). - `Massing3DScene.buildModel`: origin ← `ringsCentroid`, groundBounds ← `ringsBounds` (union всех частей), контур per-part, камера охватывает все части. - `extractPolygon`: MultiPolygon-ветка берёт **наибольшую по площади** часть (было `coordinates[0]`) → корпус строится на главной части, не осколке. Новый `extractParcelGeometry` отдаёт сцене всю геометрию. - `ConceptResultMap` (2D): `geometryBounds` фитит по всему экстенту. - `MassingScene` (ptica) area-based → не затронута. ## Test plan - [x] 264 vitest зелёные (+ geo-local multi-part кейсы: centroid между частями, bounds-union; concept-api: largest-part selection на first≠largest) - [x] tsc --noEmit чисто, next lint, prettier - [x] code-reviewer: ✅ APPROVE (single-Polygon регрессия строго исключена — делегация bit-identical) ## Post-deploy verify (rule #7) Визуальный смоук на известном многочастном участке — соседи теперь совмещены с контуром и OSM. Refs #2180
bot-backend added 1 commit 2026-07-07 08:07:49 +00:00
fix(concept): §7 massing-сцена осознаёт весь MultiPolygon участка
All checks were successful
CI / changes (pull_request) Successful in 10s
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 1m3s
CI / openapi-codegen-check (pull_request) Successful in 2m2s
9815e0607e
extractPolygon схлопывал MultiPolygon до coordinates[0], поэтому 3D/2D
massing-сцена брала начало проекции, bbox OSM-тайла и контур от ПЕРВОЙ части,
а бэкенд анкорит соседей к ЦЕЛОЙ геометрии — для многочастных участков контекст
уезжал до ~1.6км от part[0] (163 участка на проде, max 1611м).

Сцена теперь считает origin (ringsCentroid), ground bounds (ringsBounds) и
рисует контур по ВСЕМ частям (polygonOuterRings) целого Polygon|MultiPolygon;
камера охватывает все части + корпуса. Запрос /concepts по-прежнему шлёт один
Polygon, но берёт НАИБОЛЬШУЮ по площади часть, не coordinates[0] (побочный
латентный баг — part[0] мог быть осколком). 2D Leaflet-карта фитится по всему
экстенту. Одночастные участки не затронуты (helpers делегируют к прежней логике,
байт-в-байт). ptica-песочница area-based → не затронута.

Refs #2180
bot-backend merged commit 1fa99812af into main 2026-07-07 08:10:28 +00:00
bot-backend deleted branch fix/massing-multipolygon-whole-parcel 2026-07-07 08:10:28 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2463
No description provided.