Реалізація Geolocation API на сайті
Geolocation API дає координати пристрою — через GPS, WiFi триангуляцію або IP-геолокацію. Браузер завжди запрошує дозвіл користувача. Точність варіюється від метрів (GPS на вулиці) до кілометрів (IP).
Отримання поточної позиції
interface GeoPosition {
lat: number
lng: number
accuracy: number // метри
altitude?: number
speed?: number // м/с
heading?: number // градуси від півночі
timestamp: number
}
async function getCurrentPosition(
options: PositionOptions = {}
): Promise<GeoPosition> {
if (!navigator.geolocation) {
throw new Error('Geolocation API не підтримується')
}
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
(position) => {
resolve({
lat: position.coords.latitude,
lng: position.coords.longitude,
accuracy: position.coords.accuracy,
altitude: position.coords.altitude ?? undefined,
speed: position.coords.speed ?? undefined,
heading: position.coords.heading ?? undefined,
timestamp: position.timestamp,
})
},
(error) => reject(mapGeolocationError(error)),
{
enableHighAccuracy: options.enableHighAccuracy ?? false,
timeout: options.timeout ?? 10000,
maximumAge: options.maximumAge ?? 0,
}
)
})
}
function mapGeolocationError(error: GeolocationPositionError): Error {
const messages: Record<number, string> = {
1: 'Користувач заборонив доступ до геолокації',
2: 'Позиція недоступна (немає сигналу)',
3: 'Перевищено таймаут отримання позиції',
}
return new Error(messages[error.code] ?? 'Невідома помилка геолокації')
}
Слідкування за позицією
Для трекінгу маршруту, навігації або живого оновлення карти:
class GeolocationWatcher {
private watchId: number | null = null
private onPosition: (pos: GeoPosition) => void
private onError: (err: Error) => void
constructor(
onPosition: (pos: GeoPosition) => void,
onError: (err: Error) => void
) {
this.onPosition = onPosition
this.onError = onError
}
start(highAccuracy = true): void {
if (this.watchId !== null) return
this.watchId = navigator.geolocation.watchPosition(
(position) => {
this.onPosition({
lat: position.coords.latitude,
lng: position.coords.longitude,
accuracy: position.coords.accuracy,
speed: position.coords.speed ?? undefined,
heading: position.coords.heading ?? undefined,
timestamp: position.timestamp,
})
},
(error) => this.onError(mapGeolocationError(error)),
{ enableHighAccuracy: highAccuracy, timeout: 5000, maximumAge: 1000 }
)
}
stop(): void {
if (this.watchId !== null) {
navigator.geolocation.clearWatch(this.watchId)
this.watchId = null
}
}
}
React-хук
interface UseGeolocationReturn {
position: GeoPosition | null
error: string | null
loading: boolean
get: () => Promise<void>
watch: () => void
stopWatch: () => void
}
function useGeolocation(): UseGeolocationReturn {
const [position, setPosition] = useState<GeoPosition | null>(null)
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(false)
const watcherRef = useRef<GeolocationWatcher>()
useEffect(() => {
watcherRef.current = new GeolocationWatcher(
(pos) => setPosition(pos),
(err) => setError(err.message)
)
return () => watcherRef.current?.stop()
}, [])
const get = useCallback(async () => {
setLoading(true)
setError(null)
try {
const pos = await getCurrentPosition({ enableHighAccuracy: true })
setPosition(pos)
} catch (e) {
setError(e instanceof Error ? e.message : 'Помилка геолокації')
} finally {
setLoading(false)
}
}, [])
const watch = useCallback(() => watcherRef.current?.start(), [])
const stopWatch = useCallback(() => watcherRef.current?.stop(), [])
return { position, error, loading, get, watch, stopWatch }
}
Вичислення відстані (формула Хаверсина)
Часто потрібна рядом з геолокацією — знайти найближчі об'єкти, показати дистанцію:
function haversineDistance(
a: { lat: number; lng: number },
b: { lat: number; lng: number }
): number {
const R = 6371000 // радіус Землі в метрах
const toRad = (deg: number) => (deg * Math.PI) / 180
const dLat = toRad(b.lat - a.lat)
const dLng = toRad(b.lng - a.lng)
const ha =
Math.sin(dLat / 2) ** 2 +
Math.cos(toRad(a.lat)) * Math.cos(toRad(b.lat)) * Math.sin(dLng / 2) ** 2
return R * 2 * Math.atan2(Math.sqrt(ha), Math.sqrt(1 - ha))
}
// Знайти найближчий об'єкт
function findNearest<T extends { lat: number; lng: number }>(
current: { lat: number; lng: number },
points: T[]
): T | null {
if (!points.length) return null
return points.reduce((nearest, point) =>
haversineDistance(current, point) < haversineDistance(current, nearest)
? point
: nearest
)
}
Що входить у роботу
Утилітипи отримання та слідкування позиції, коректна обробка помилок (відмова в дозволі, таймаут, недоступність), React-хук, вичислення відстаней, інтеграція з картою (Leaflet, Google Maps, Mapbox) за потреби.
Строк: 0.5–1 день. З картою — 1–2 дні.







