feat(tradein): MapPicker snap marker — Phase 5 of #582 #594

Merged
Light1YT merged 1 commit from feat/tradein-mappicker-snap-marker into main 2026-05-27 09:30:17 +00:00
Owner

Summary

Phase 5 of issue #582 — marker на карте теперь совпадает с canonical address в карточке.

Bug fix

Do PR: /api/v1/geocode/reverse echo'ил input lat/lon → marker оставался где user кликнул (проезд/двор), а address resolved до canonical ул. Малышева, 125. Visual mismatch.

После PR: backend возвращает snapped point matched здания, frontend двигает marker если precision = exact/number/cadastral и distance >5m.

Changes (+677 / -37)

Backend:

  • services/geocoder.py (+208/-16): reverse_geocode → ReverseGeocodeResult (address + snapped + precision + provider). Cadastral FDW → Yandex → Nominatim chain. kind=house для Yandex /reverse.
  • api/v1/geocode.py (+38/-8): новый Pydantic ReverseResponse model.
  • tests/test_geocode_reverse_api.py (new, 228 LOC): 7 tests — endpoint shape, 404, precision differentiation, Yandex parsing.
  • tests/services/test_cadastral_reverse.py (+98/-28): 6 updated + 2 new (Yandex path + Yandex→Nominatim fallthrough).

Frontend:

  • components/trade-in/MapPicker.tsx (+82/-8): SNAP_PRECISIONS set + SNAP_MIN_DISTANCE_M=5 + haversineMeters(). После reverse — marker.setLatLng + map.panTo (smooth). Hint «Точка дома по Яндексу» с aria-live + var(--fg-tertiary) token.

Tests

  • 37 backend tests pass
  • ruff clean
  • tsc --noEmit clean
  • code-reviewer LGTM (1 nit applied inline: hex → token)

Post-deploy QA

  • Открыть https://gendsgn.ru/trade-in/ с pilottest creds
  • Click в проезде у Малышева 125 → marker должен snap to building + hint «Точка дома по Яндексу»
  • Click в проезде где address resolves до street (precision=street) → marker остаётся где click, hint НЕ показывается
  • Verify в DevTools network: response содержит snapped_lat/snapped_lon/precision/provider

Closes #582 (Phase 5).

## Summary **Phase 5 of issue #582** — marker на карте теперь совпадает с canonical address в карточке. ### Bug fix Do PR: `/api/v1/geocode/reverse` echo'ил input lat/lon → marker оставался где user кликнул (проезд/двор), а address resolved до canonical `ул. Малышева, 125`. Visual mismatch. После PR: backend возвращает snapped point matched здания, frontend двигает marker если precision = exact/number/cadastral и distance >5m. ## Changes (+677 / -37) **Backend:** - `services/geocoder.py` (+208/-16): `reverse_geocode → ReverseGeocodeResult` (address + snapped + precision + provider). Cadastral FDW → Yandex → Nominatim chain. `kind=house` для Yandex /reverse. - `api/v1/geocode.py` (+38/-8): новый Pydantic `ReverseResponse` model. - `tests/test_geocode_reverse_api.py` (new, 228 LOC): 7 tests — endpoint shape, 404, precision differentiation, Yandex parsing. - `tests/services/test_cadastral_reverse.py` (+98/-28): 6 updated + 2 new (Yandex path + Yandex→Nominatim fallthrough). **Frontend:** - `components/trade-in/MapPicker.tsx` (+82/-8): `SNAP_PRECISIONS` set + `SNAP_MIN_DISTANCE_M=5` + `haversineMeters()`. После reverse — `marker.setLatLng + map.panTo` (smooth). Hint «Точка дома по Яндексу» с aria-live + var(--fg-tertiary) token. ## Tests - 37 backend tests pass - ruff clean - tsc --noEmit clean - code-reviewer LGTM (1 nit applied inline: hex → token) ## Post-deploy QA - [ ] Открыть https://gendsgn.ru/trade-in/ с pilottest creds - [ ] Click в проезде у Малышева 125 → marker должен snap to building + hint «Точка дома по Яндексу» - [ ] Click в проезде где address resolves до street (precision=street) → marker остаётся где click, hint НЕ показывается - [ ] Verify в DevTools network: response содержит `snapped_lat/snapped_lon/precision/provider` Closes #582 (Phase 5).
Light1YT added 1 commit 2026-05-27 09:30:15 +00:00
Backend /api/v1/geocode/reverse теперь возвращает snapped_lat/snapped_lon
центра matched здания (от Yandex Point.pos / Nominatim response.lat-lon /
cadastral row) + precision. Раньше endpoint echo'ил input lat/lon —
адрес resolved'ился до canonical «ул. Малышева, 125», но marker
оставался где кликнул user (часто в проезде / дворе).

Backend:
- reverse_geocode: str | None → ReverseGeocodeResult | None
  (address + snapped_lat/snapped_lon + precision + provider).
  Provider fallback Cadastral FDW → Yandex (если key) → Nominatim.
  Yandex /reverse дёргаем с kind=house чтобы сразу получить здание.
- /api/v1/geocode/reverse: новый Pydantic ReverseResponse model.
- snap_precision_useful() helper для frontend logic parity.
- Legacy _cadastral_reverse_sync сохранён для backward compat.

Frontend MapPicker:
- После reverse — если precision in (exact, number, cadastral) и
  snapped >5m от клика → marker.setLatLng + map.panTo с animate
- precision=street/range/locality → marker остаётся где кликнули
  (не врём что нашли точное здание).
- Hint «Точка дома по Яндексу» под address с aria-live="polite"
  (var(--fg-tertiary) — token compliance).
- haversineMeters helper для distance check (6371000m Earth radius).

Tests: 37 backend pass, ruff + tsc clean.
Code-reviewer LGTM, 1 nit applied inline (hex → var(--fg-tertiary)).

Closes #582 (Phase 5).
Light1YT merged commit 42ab74e2e8 into main 2026-05-27 09:30:17 +00:00
Light1YT deleted branch feat/tradein-mappicker-snap-marker 2026-05-27 09:30:17 +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#594
No description provided.