atomic-map-coordinates-inconsistency.md 3.1 KB

[lng, lat] vs [lat, lng] — неконсистентный порядок координат в MapProvider

Контекст: В frontend/src/lib/map.ts интерфейс MapProvider имеет методы, ожидающие разный порядок координат: init() принимает [lng, lat], а setCenter()[lat, lng].

Проблема

// lib/map.ts — интерфейс (не документирует порядок!)
interface MapProvider {
    init(container: HTMLElement, center: [number, number], zoom: number): Promise<void>
    setCenter(lat: number, lng: number): void
    onMove(cb: (center: [number, number], zoom: number, bounds: MapBounds) => void): void
}
// lib/map.ts — реализация init: ожидает [lng, lat]
init(container, center, zoom) {
    map = L.map(container).setView([center[1], center[0]], zoom) // 🌀 swap
}

// lib/map.ts — реализация setCenter: ожидает [lat, lng]
setCenter(lat, lng) {
    map?.setView([lat, lng]) // без swap
}

// onMove callback: возвращает [lng, lat]
moveHandler([c.lng, c.lat], map.getZoom(), bounds)
// MapView.tsx — вызов: передаёт [lng, lat] (Moscow)
provider.init(containerRef.current, [37.6173, 55.7558], 10)

Почему это проблема:

  1. init() принимает [lng, lat], но setCenter()[lat, lng]
  2. onMove() возвращает [lng, lat] — если кто-то использует center из колбэка, он получит неожиданный порядок
  3. Тип [number, number] не документирует порядок — новый разработчик гарантированно ошибётся

Решение

Вариант A (рекомендую): Унифицировать через именованный объект { lat, lng }:

interface LatLng { lat: number; lng: number }

interface MapProvider {
    init(container: HTMLElement, center: LatLng, zoom: number): Promise<void>
    setCenter(center: LatLng): void
    onMove(cb: (center: LatLng, zoom: number, bounds: MapBounds) => void): void
}

Вариант B: Строгий тип с документированным порядком:

/** [lng, lat] — внимание: порядок как в GeoJSON! */
type LngLat = [lng: number, lat: number]

interface MapProvider {
    init(container: HTMLElement, center: LngLat, zoom: number): Promise<void>
}

Почему возникло

Leaflet использует [lat, lng]. GeoJSON использует [lng, lat]. API-дизайнеры часто путаются. В проекте был взят GeoJSON-стиль для init() (потому что bounds приходят как "lng,lat" из API), но setCenter() остался в Leaflet-стиле.

Связанные заметки

  • [[architecture-overview]] — таблица P0
  • [[frontend-api-client]] — API клиент

Источник

Code review PhotoPlaces 2026-06. lib/map.ts и MapView.tsx.

Теги

#frontend #leaflet #maps #api-design #inconsistency #clean-code