feat(tradein/v2): карта вторички — сделки Росреестра + объявления по источникам (SourcesView) #2478

Merged
bot-backend merged 1 commit from feat/v2-secondary-map into main 2026-07-12 13:26:56 +00:00
Collaborator

Что сделано

В overlay «05 АНАЛОГИЧНЫЕ ОБЪЯВЛЕНИЯ» (/v2 → SectionOverlay → SourcesView) добавлена карта SourcesMap.tsx, которая рисует поверх реального OSM-баземапа (Leaflet с CDN, без npm-зависимости — тот же паттерн, что и в MapCard.tsx):

  • estimate.analogs — объявления, точка окрашена по источнику (avito/cian/yandex/domklik/n1), легенда динамическая (только реально присутствующие источники — без фейковых записей);
  • estimate.actual_deals — сделки Росреестра, ромбовидный маркер нейтрального цвета (форма вместо ещё одного оттенка палитры);
  • целевая квартира + радиус-круг (радиус = тот же максимум distance_m, что уже заявлен в фильтр-чипе таблицы объявлений выше — карта не вводит другую цифру);
  • попапы: адрес, ₽/м², цена, площадь/комнаты, источник, дата (для сделок — период по кварталу с явной пометкой, для объявлений — день + срок экспозиции), ссылка на оригинал где есть (через safeUrl).

Честность по сделкам (street-centroid)

Сделки Росреестра геокодируются по street-centroid (geocode_deals_from_houses.py) — несколько сделок на одной улице схлопываются в одну точку. Совпадающие координаты группируются, точки визуально разносятся детерминированным jitter’ом (~12-15 м, не меняет честность гео-привязки) и получают бейдж «×N»; под картой — явная подпись про приближение. Данные конкретно в estimate.actual_deals ограничены top-10 на оценку (estimator.py: deals[:10]), так что внутри одной карты сильного наложения обычно немного, но группировка отрабатывает и для общегородского stacking, если он попадёт в выборку.

Проверка формы данных (по заданию)

Проверено ДО написания кода: types/trade-in.ts (AnalogLot.lat/lon/source, использует и analogs, и actual_deals) + бэкенд (schemas/trade_in.py::AnalogLot, estimator.py::_listing_to_analog/_deal_to_analog, SQL в _fetch_deals уже селектит lat, lon). Координаты уже есть в живом ответе /estimate для обоих массивов — бэкенд не менялся.

Файлы

  • tradein-mvp/frontend/src/components/trade-in/v2/SourcesMap.tsx — новый компонент.
  • tradein-mvp/frontend/src/components/trade-in/v2/SourcesView.tsx — монтирует SourcesMap над таблицами, новый опциональный проп estimate.
  • tradein-mvp/frontend/src/components/trade-in/v2/SectionOverlay.tsx — прокидывает estimate дальше в SourcesView.
  • tradein-mvp/frontend/src/app/v2/page.tsx — передаёт реальный estimate в SectionOverlay.
  • tradein-mvp/frontend/src/components/trade-in/v2/mappers.ts — несколько приватных форматтеров экспортированы (fmtMonthYear/numRu/fmtDist/sourceLabel/tierLabel/adMeta/dealMeta/deglueAddr) для переиспользования в попапах карты вместо дублирования логики.

Test plan

  • npm run type-check — чисто.
  • npm run lint (next lint) — чисто (только 3 pre-existing warning в несвязанных файлах).
  • npm run build — прод-сборка проходит, /v2 роут собирается.
  • Ручная проверка в браузере (не делалась в этой сессии — рекомендуется перед мержем прогнать реальную оценку и открыть overlay «05», чтобы визуально проверить баземап/попапы/jitter на живых данных).
## Что сделано В overlay «05 АНАЛОГИЧНЫЕ ОБЪЯВЛЕНИЯ» (`/v2` → SectionOverlay → SourcesView) добавлена карта `SourcesMap.tsx`, которая рисует поверх реального OSM-баземапа (Leaflet с CDN, без npm-зависимости — тот же паттерн, что и в `MapCard.tsx`): - **estimate.analogs** — объявления, точка окрашена по источнику (avito/cian/yandex/domklik/n1), легенда динамическая (только реально присутствующие источники — без фейковых записей); - **estimate.actual_deals** — сделки Росреестра, ромбовидный маркер нейтрального цвета (форма вместо ещё одного оттенка палитры); - целевая квартира + радиус-круг (радиус = тот же максимум `distance_m`, что уже заявлен в фильтр-чипе таблицы объявлений выше — карта не вводит другую цифру); - попапы: адрес, ₽/м², цена, площадь/комнаты, источник, дата (для сделок — период по кварталу с явной пометкой, для объявлений — день + срок экспозиции), ссылка на оригинал где есть (через `safeUrl`). ### Честность по сделкам (street-centroid) Сделки Росреестра геокодируются по street-centroid (`geocode_deals_from_houses.py`) — несколько сделок на одной улице схлопываются в одну точку. Совпадающие координаты группируются, точки визуально разносятся детерминированным jitter’ом (~12-15 м, не меняет честность гео-привязки) и получают бейдж «×N»; под картой — явная подпись про приближение. Данные конкретно в `estimate.actual_deals` ограничены top-10 на оценку (`estimator.py: deals[:10]`), так что внутри одной карты сильного наложения обычно немного, но группировка отрабатывает и для общегородского stacking, если он попадёт в выборку. ### Проверка формы данных (по заданию) Проверено ДО написания кода: `types/trade-in.ts` (`AnalogLot.lat/lon/source`, использует и `analogs`, и `actual_deals`) + бэкенд (`schemas/trade_in.py::AnalogLot`, `estimator.py::_listing_to_analog/_deal_to_analog`, SQL в `_fetch_deals` уже селектит `lat, lon`). **Координаты уже есть в живом ответе `/estimate` для обоих массивов — бэкенд не менялся.** ## Файлы - `tradein-mvp/frontend/src/components/trade-in/v2/SourcesMap.tsx` — новый компонент. - `tradein-mvp/frontend/src/components/trade-in/v2/SourcesView.tsx` — монтирует `SourcesMap` над таблицами, новый опциональный проп `estimate`. - `tradein-mvp/frontend/src/components/trade-in/v2/SectionOverlay.tsx` — прокидывает `estimate` дальше в `SourcesView`. - `tradein-mvp/frontend/src/app/v2/page.tsx` — передаёт реальный `estimate` в `SectionOverlay`. - `tradein-mvp/frontend/src/components/trade-in/v2/mappers.ts` — несколько приватных форматтеров экспортированы (`fmtMonthYear`/`numRu`/`fmtDist`/`sourceLabel`/`tierLabel`/`adMeta`/`dealMeta`/`deglueAddr`) для переиспользования в попапах карты вместо дублирования логики. ## Test plan - [x] `npm run type-check` — чисто. - [x] `npm run lint` (next lint) — чисто (только 3 pre-existing warning в несвязанных файлах). - [x] `npm run build` — прод-сборка проходит, `/v2` роут собирается. - [ ] Ручная проверка в браузере (не делалась в этой сессии — рекомендуется перед мержем прогнать реальную оценку и открыть overlay «05», чтобы визуально проверить баземап/попапы/jitter на живых данных).
bot-backend added 1 commit 2026-07-12 13:23:08 +00:00
feat(tradein/v2): карта вторички в overlay 05 — сделки Росреестра + объявления по источникам
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m5s
293423649a
SourcesMap.tsx: реальный OSM-баземап (Leaflet CDN, порт паттерна MapCard.tsx,
без npm-зависимости) поверх estimate.analogs + estimate.actual_deals — оба
поля уже несут lat/lon и source в живом API-ответе (проверено по
_listing_to_analog/_deal_to_analog в estimator.py и AnalogLot в
schemas/trade_in.py), бэкенд не менялся.

Объявления — цвет по источнику (categorical-палитра dataviz-skill, validated),
сделки — ромб нейтрального цвета вместо ещё одного hue (сделки геокодированы
по street-centroid и стекируются в одну точку — сгруппированы + jitter +
бейдж «×N», честная подпись про приближение). Целевая квартира + радиус-круг
по той же дистанции, что уже заявлена в фильтр-чипе таблицы объявлений выше.

Смонтирован в SourcesView.tsx (overlay «05 АНАЛОГИЧНЫЕ ОБЪЯВЛЕНИЯ»), estimate
прокинут через SectionOverlay из app/v2/page.tsx. mappers.ts: несколько
приватных форматтеров (fmtMonthYear/numRu/fmtDist/sourceLabel/tierLabel/
adMeta/dealMeta/deglueAddr) экспортированы для переиспользования в попапах
карты вместо дублирования.
bot-backend merged commit 8dd35c01d8 into main 2026-07-12 13:26:56 +00:00
bot-backend deleted branch feat/v2-secondary-map 2026-07-12 13:26:57 +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#2478
No description provided.