Всім привіт! Я, як і багато хто тут, не лише програміст, але й великий цінитель активного відпочинку. Велосипед, походи, гори — усе це потребує ретельного планування. І хоча існує безліч класних сервісів, мені завжди хотілося чогось більшого: платформи, яка об’єднувала б у собі гнучкий інструмент для створення маршрутів, базу знань про цікаві місця та спільноту однодумців.
Так я почав самостійно створювати The Peakline — свій великий проект для аутдор-ентузіастів. Однією з центральних та найскладніших частин цієї системи мав стати планувальник маршрутів. Я вирішив зробити його максимально функціональним та відкритим, щоб він став вітриною можливостей всього проекту.
У цій статті я хочу провести вас «за куліси» і показати, як влаштований фронтенд саме цієї частини мого проекту. Ми зануримося в архітектуру на чистому JavaScript, поговоримо про інтеграцію з картографічними API та про ті неочевидні проблеми, з якими стикаєшся, коли створюєш такий продукт в одиночку.
Важливий дисклеймер: Весь проект, від ідеї до коду, я роблю один у вільний від основної роботи час. Він далек від ідеалу (і досить близько), і я буду дуже вдячний за конструктивну критику та свіжий погляд.
Тут ви можете одразу ознайомитися з докладними записами роботи з планувальником.
Запрошую вас вивчити як сам проект, так і його ключову фічу:
- Основний проект The Peakline: https://www.thepeakline.com/
- Планувальник маршрутів (про який стаття): https://www.thepeakline.com/route-planner
- Репозиторій на GitHub: https://github.com/CyberScoper/peakline-route-planner
А тепер — до технічних деталей.
Загальний вигляд інтерфейсу планувальника в ThePeakline
Архітектура: чому Vanilla JS і як не заплутатися у коді
Перше і головне питання: чому не React/Vue/Svelte? Відповідь проста: я хотів повного контролю та мінімального оверхеда. Робота з картами — це часто пряме маніпулювання шарами, маркерами й подіями, які надає сама бібліотека карт (у моєму випадку Leaflet). Обгортати усе це в реактивну модель фреймворку здавалося зайвим ускладненням. До того ж, це був чудовий виклик — побудувати керований застосунок на «чистому» JavaScript.
Щоб не тонути в «лапші» з колбеків, я розділив всю логіку на три смислові модулі, реалізовані як IIFE (Immediately Invoked Function Expression) для інкапсуляції стану:
route-planner.js
(View/Controller): Цей модуль — диригент оркестру. Він ініціює Leaflet, відрисовує UI, слухає всі дії користувача (клік по карті, натискання кнопок, зміни в полях вводу) і делегує обробку логіки «Мозку». Він єдиний, хто «знає» про існування DOM.route-manager.js
(Model): Це «мозок» і єдине джерело правди (Single Source of Truth). Він зберігає масив точок маршруту, його метадані (назва, тип), розраховану статистику. Він надає публічний API для зміни цих даних (addPoint
,undo
,setRoutingEngine
), але нічого не знає про те, як ці дані відображаються.route-export.js
(Service): Чистий, без стану, модуль-конвертер. Його задача — взяти дані зroute-manager
і перетворити їх у рядки форматів GPX, KML або TCX.
Комунікація між ними проста: route-planner
викликає публічні методи route-manager
. Після кожної зміни стану route-planner
запитує актуальні дані у route-manager
і повністю перерисовує все, що потрібно, на карті та в UI. Просто, але ефективно.
Під капотом «Аналіз маршруту»: як симуляція оживляє дані
Однією з функцій, якою я пишаюся найбільше, є «Аналіз маршруту». Мені хотілося, щоб користувач отримував не просто сухі цифри дистанції, а повне уявлення про майбутній шлях: яке покриття його чекає, наскільки складним буде маршрут і які рекомендації можна дати.
Проблема в тому, що отримати реальні дані про тип покриття для кожної точки маршруту в режимі реального часу — задача майже невиконувана без доступу до дорогих комерційних GIS-API. Тому я обрав інший шлях: створив систему реалістичної симуляції.
Аналіз треку.
Крок 1: Точка входу — analyzeRouteSurface()
Усе починається, коли користувач натискає кнопку «🔍 Аналіз». Це викликає асинхронну функцію analyzeRouteSurface()
у route-planner.js
. Її задача проста:
- Перевірити, що в маршруті є хоча б 2 точки.
- Очистити попередні результати аналізу.
- Запустити генерацію «mock» (симульованих) даних про поверхню.
- Відобразити результати на карті та в спеціальній панелі.
Крок 2: Мозок симуляції — generateMockSurfaceAnalysis()
Це серце всієї системи. Замість того щоб просто видавати випадкові дані, я намагався зробити симуляцію розумною і залежною від контексту. Функція generateRealisticSurface()
враховує тип активності користувача, який визначається за середньою швидкістю, встановленою в налаштуваннях.
- Якщо користувач — велосипедист (швидкість > 20 км/год), симуляція з більшою ймовірністю генеруватиме ділянки з асфальтом.
- Якщо користувач — пішохідний турист (швидкість < 8 км/год), у маршруті з'явиться більше ґрунту та стежок.
Ось як це виглядає в коді (упрощено):
Для кожного сегмента маршруту (ділянка між двома точками, поставленими вручну) генерується тип поверхні, її стан, складність (від 1 до 5) та придатність (для якого виду активності підходить).
Крок 3: Візуалізація — createSurfaceAnalysisVisualization() та showSurfaceAnalysisResults()
Після генерації даних їх потрібно красиво показати.
- На карті: Функція
createSurfaceAnalysisVisualization()
оббігає кожен симульований сегмент і малює поверх основної лінії маршруту товсту кольорову полілінію. Колір залежить від типу покриття (асфальт — зелений, ґрунт — оранжевий, гравій — червоний), а для поганого стану додається пунктирний стиль. - У панелі аналізу: Метод
showSurfaceAnalysisResults()
відповідає за рендеринг цієї самої красивої панелі зі статистикою: - Загальна інформація: Розраховує та виводить сукупні дані (середня складність, час проходження).
- Розподіл поверхонь: Будує наочний прогрес-бар, що показує відсоткове співвідношення типів покриття.
- Детальний аналіз сегментів: Створює таблицю, де кожен сегмент маршруту розглядають за запчастинами.
- Рекомендації: Найбільш розумна частина. Це блок
if-else
правил, який аналізує отримані дані та дає поради. Наприклад: - Якщо більш ніж 70% маршруту підходить для велосипеда, він пише «Чудово для велосипеда!».
- Якщо середня складність вище 3.5, попереджає «Висока складність».
- Якщо в маршруті багато гравію, може порадити «Розгляньте шини з хорошим зчепленням».
Таким чином, функція аналізу — це не просто показ випадкових картинок, а ціла система, яка намагається бути контекстно-залежною та давати користувачу дійсно корисну, хоч і симульовану, інформацію. На мою думку, це відмінний приклад того, як можна збагачувати користувацький досвід, навіть не маючи доступу до ідеальних даних.
route-planner.js: Магія інтерактивної карти
Серце застосунку — це, звичайно, карта. Я обрав Leaflet.js за його легкість, велику кількість плагінів та простоту API.
1. Ініціалізація та шари
Все починається стандартно. Але важливо не забути про можливість зміни підкладки — це сильно підвищує зручність.
Карта з відкритим перемикачем шарів
2. Інтеграція з рушіями маршрутизації
Це ключова фіча. У мене є ручний режим (точки з'єднуються прямими лініями) і автоматичний, який використовує зовнішні API. Як це працює?
Коли користувач клікає по карті, route-planner
дивиться, який режим зараз активний.
- Ручний режим: Просто додаємо точку в
route-manager
. - Автоматичний режим (OSRM, OpenRouteService та ін.): Тут усе цікавіше. Нам потрібна не тільки остання точка, але й попередня, щоб побудувати сегмент маршруту між ними.
Крупний план панелі режимів і налаштувань
route-manager.js: Хранитель стану
Ц
Коментарі