# Як шукати найближчі зупинки транспорту за допомогою API?

> Дізнайтеся, як знаходити найближчі зупинки за допомогою API ROOTE: координати, радіус, приклад JavaScript, обробка результатів та помилок.

Source: https://www.roote.ai/uk/guides/yak-znajty-zupynky-transportu-poblyzy-iz-api-roote/
Language: uk
Author: ROOTE

Щоб знайти зупинки навколо точки, передайте її широту, довготу та радіус у API близькості. Потім перевірте статус відповіді, отримані сутності та інформацію про покриття перед відображенням списку або карти.

У контракті ROOTE roote-1.0.0 маршрут GET /v1/transit/nearby знаходить найближчі транспортні пункти. Він не отримує інформацію про відправлення або сповіщення у реальному часі. Пошук локації та пошук її наступного проходження — це дві окремі операції.

## Визначення параметрів

Запит використовує lat для широти та lng для довготи. Псевдонім lon також описаний у контракті. Параметр radius означає радіус у метрах; limit обмежує кількість запитаних результатів. Фільтр modes може вказувати типи транспорту.

| Параметр | Приклад | Значення |
| --- | --- | --- |
| lat | 44.8378 | Широта точки пошуку |
| lng | -0.5792 | Довгота точки пошуку |
| radius | 600 | Запитуваний радіус у метрах |
| limit | 10 | Запитуваний ліміт результатів |
| modes | bus,tram | Типи транспорту |

Ці координати є прикладом для пошуку у Бордо; вони не гарантують існування зупинки. Перегляньте [контракт OpenAPI ROOTE](https://api.roote.ai/openapi.json) для діючих меж, полів та умов.

## Відправка першого запиту на сервері

Ось приклад JavaScript для середовища Node.js із підтримкою fetch. Токен, якщо ваш доступ його вимагає, зберігається у змінній середовища на сервері. Приклад не потребує розміщення секрету у браузері.

```
async function rechercherArrets(token = process.env.ROOTE_API_TOKEN) {
  const url = new URL('https://api.roote.ai/v1/transit/nearby');
  url.search = new URLSearchParams({
    lat: '44.8378',
    lng: '-0.5792',
    radius: '600',
    limit: '10',
    modes: 'bus,tram'
  }).toString();

  const headers = { Accept: 'application/json' };
  if (token) headers.Authorization = `Bearer ${token}`;

  const response = await fetch(url, {
    headers,
    signal: AbortSignal.timeout(10000)
  });
  if (!response.ok) {
    throw new Error(`Erreur HTTP ${response.status}`);
  }

  const data = await response.json();
  if (data.contract_version !== 'roote-1.0.0') {
    throw new Error('Version du contrat non reconnue');
  }
  if (!['success', 'empty', 'partial'].includes(data.status)) {
    throw new Error('Recherche indisponible');
  }
  if (!Array.isArray(data.stations)) {
    throw new Error('Réponse sans collection stations valide');
  }

  return {
    status: data.status,
    stations: data.stations,
    lines: data.lines,
    operators: data.operators,
    coverage: data.coverage,
    warnings: data.warnings,
    attributions: data.attributions,
    meta: data.meta
  };
}
```

Описаний контракт передбачає анонімний або токен-авторизований доступ залежно від політики. Перевірте свої права та обмеження доступу. Коректна HTTP-відповідь не гарантує валідність вмісту; у продакшені також застосовуйте валідацію об'єктів відповідно до схеми.

## Читання сутностей та їхніх зв’язків

Колекція stations містить повернені локації. Для кожної перевірте id, name, entity_kind, location та distance_meters. Посилання line_ids і operator_ids дозволяють пов’язати колекції lines і operators, якщо вони є.

Відображайте географічну відстань як таку. Не конвертуйте її у час ходьби без маршрутизації. Посібник [знайти найближчу зупинку](https://www.roote.ai/uk/guides/yak-znayty-nayblyzhchu-zupynku-avtobusa-abo-tramu/) пояснює, чому доступи можуть впливати на реальний шлях.

Також явно обробляйте невідому інформацію. За контрактом, accessibility.wheelchair може бути unknown: це не означає ні так, ні ні. Запланована кількість відправлень не є списком відправлень.

## Відображення списку або карти

Використовуйте ідентифікатор для стабільності елементів інтерфейсу, назву — для підпису, а location — для позиції. Пов’язуйте лінії через посилання, а не по іменах.

Якщо відображаєте кольори ліній або підписи з даних, обробляйте їх як зовнішні вхідні дані, що вимагають валідації. Для імен використовуйте текст, а не інжектований HTML.

Зберігайте атрибуції джерел і показуйте ті, які контракт вимагає.

## Обробка порожніх результатів, часткових відповідей і помилок

Результат empty означає відсутність результатів у відомому периметрі. Це не свідчить про фізичну відсутність транспорту. Відповідь partial може містити корисні місця, але вказує на обмеження: показуйте результати та відповідне попередження.

Читайте coverage, warnings і межі, задані в meta. Обрізаний список не описує повне покриття. У разі помилки мережі або HTTP показуйте статус недоступності, не замінюючи результат на «жодних зупинок».

Для коду 429 дотримуйтесь інструкцій відновлення та відповідних заголовків сервісу. Уникайте циклічних повторних запитів.

## Розрізнення станцій, зон і платформ

Поле entity_kind розрізняє кілька рівнів локацій. Два сусідні результати можуть бути різними платформами; два схожі назви можуть належати різним джерелам.

Не об’єднуйте локації лише за близькістю. Використовуйте задокументовані зв’язки та ідентичності сервісу. Наш посібник [GTFS, GTFS-RT і GBFS](https://www.roote.ai/uk/guides/gtfs-gtfs-rt-ta-gbfs-yaki-vidminy/) пояснює контекст даних.

## Підготовка інтеграції у продакшн

Запускайте пошук, коли позиція або фільтри змінюються значно. Групуйте однакові виклики, задавайте таймаути і адаптуйте кеш відповідно до типу даних та умов сервісу.

Список локацій та реальний час мають різні вимоги до свіжості. Перевірте обробку повних, порожніх, часткових та помилкових відповідей перед подачею пошуку користувачам.

## Розширення пошуку на міські послуги

Зупинки та міські послуги використовують різні маршрути. Для пошуку туалетів біля того самого місця, маршрут GET /v1/services/nearby чекає lat і lon, з параметром types=toilets. Не надсилайте modes=toilets до цього маршруту: ця термінологія належить URL карти, а не фільтру послуг.

Наступний приклад на JavaScript будує URL для Сервісів з радіусом 600 метрів. Запит не виконується; повторно використовуйте HTTP-контролі та контракти, описані вище. Очікувана колекція — services, замість stations. Зберігайте service_type, location, distance_meters та фактично наявні атрибути.

REST-контракт документує зокрема toilets, drinking_water, fountain, wifi, parking, charging, aed та locker. Типи, які оприлюднює MCP, можуть відрізнятись. Для прийнятих параметрів, їх меж та обмежень вашого доступу звертайтесь до схеми використаного інтерфейсу.

Атрибути послуги не гарантують її доступність під час пошуку. Невідома доступність не означає, що послуга недоступна; порожній список через помилку не доводить відсутність туалетів. Зберігайте дані окремо для кожної категорії, а не зводьте до назви та точки.

Для комбінованої карти співставляйте результати з їх категорією та ідентифікаторами. Відображайте помилку Сервісів, не видаляючи зупинки, повернені Transit. Пошук залишається зосередженим на тому самому пункті, але статуси та покриття можуть відрізнятись.

```
const url = new URL('https://api.roote.ai/v1/services/nearby');
url.search = new URLSearchParams({
  lat: '44.8416106', lon: '-0.5810938',
  radius: '600', limit: '10', types: 'toilets'
}).toString();
console.log(url.toString());
```

[Діагностика порожнього або неправильного пошуку](https://www.roote.ai/uk/guides/vidchynyj-rezultat-chy-pomylka-api-yak-rozyznyty/)

[Безпосереднє впровадження фільтрованої карти на сайт](https://www.roote.ai/uk/guides/yak-integruvaty-kartu-mobilnosti-u-svij-sajt/)

[Створення помічника на основі цих пошуків](https://www.roote.ai/uk/guides/yak-stvoriti-asistenta-yakij-znajde-mobilnist-navkolo-adresi/)

## Поширені питання

### Чи показує Nearby наступні відправлення?

Ні у цьому контракті. Цей маршрут знаходить транспортні пункти; відправлення вимагають окремих можливостей.

### Чи можна показувати порожній список після помилки?

Показуйте статус недоступності. Помилка не доводить відсутність зупинок.

### Чи можна розміщувати API токен у браузері?

Секрет повинен залишатися на сервері. Використовуйте передбачену модель доступу для вашого додатку та аккаунту.
