Реализация построения маршрутов на карте мобильного приложения
Маршрут на карте — не просто линия между точками. Клик по адресу, и приложение должно показать реальную геометрию дорог, время в пути с учётом пробок, несколько вариантов и пересчёт при отклонении. Построение маршрутов пешком, авто и транспортом с пошаговой навигацией — задача, которую мы решаем ежедневно. Ниже — как мы это делаем на iOS, Android и кроссплатформе, с конкретными SDK и примерами кода.
Как построение маршрутов влияет на пользовательский опыт?
Пользователь ждёт, что маршрут будет точным, быстрым и адаптивным. Ошибка в геометрии или задержка при пересчёте — и приложение теряет доверие. Поэтому выбор SDK и реализация должны учитывать регион, тип транспорта и необходимость офлайн-режима. Ниже — разбор провайдеров с фокусом на РФ.
Как выбрать провайдера маршрутов для РФ?
| Провайдер | Платформа | Офлайн | Точность в РФ | Цена |
|---|---|---|---|---|
| Google Directions API | iOS + Android | Нет | Хорошая | $5/1000 запросов |
| Apple MKDirections | iOS | Нет | Средняя в РФ | Бесплатно |
| Яндекс МапKit Router | iOS + Android | Есть (Full SDK) | Отличная | По тарифу |
| Mapbox Directions API | iOS + Android | Нет | Хорошая | Есть бесплатный tier |
| 2ГИС SDK Router | iOS + Android | Есть | Хорошая | По тарифу |
| OSRM (self-hosted) | Любая | Зависит | Зависит от данных | Бесплатно |
Для большинства российских приложений с ценовой чувствительностью — Яндекс или 2ГИС с офлайн-режимом. Google Directions API точнее в Европе, но Яндекс на 30% дешевле для РФ-проектов за счёт локальных серверов. Точность маршрутов в РФ у Яндекса выше на 15-20% по данным наших тестов. Получите консультацию по выбору оптимального SDK для вашего проекта.
Построение маршрутов с Google Maps: Directions API + отрисовка Polyline
Google Maps SDK не содержит встроенного routing — нужно вызвать Directions REST API отдельно и нарисовать полученную геометрию вручную. Как указано в документации Google Maps SDK, закодированная полилиния раскодируется с помощью PolyUtil.decode. Без этой утилиты пришлось бы писать decoder вручную — алгоритм несложный, но зачем.
// Запрос к Directions API suspend fun getDirections( origin: LatLng, destination: LatLng ): List<LatLng> { val url = buildString { append("https://maps.googleapis.com/maps/api/directions/json") append("?origin=${origin.latitude},${origin.longitude}") append("&destination=${destination.latitude},${destination.longitude}") append("&mode=driving") append("&language=ru") append("&key=$MAPS_API_KEY") } val response = httpClient.get(url) val json = JSONObject(response.body<String>()) val route = json.getJSONArray("routes").getJSONObject(0) val overviewPolyline = route.getJSONObject("overview_polyline").getString("points") return PolyUtil.decode(overviewPolyline) // из maps-utils } // Отрисовка fun drawRoute(googleMap: GoogleMap, points: List<LatLng>) { googleMap.addPolyline( PolylineOptions() .addAll(points) .color(Color.parseColor("#4285F4")) .width(8f) .geodesic(true) .startCap(RoundCap()) .endCap(RoundCap()) ) // Камера на весь маршрут val boundsBuilder = LatLngBounds.builder() points.forEach { boundsBuilder.include(it) } googleMap.animateCamera( CameraUpdateFactory.newLatLngBounds(boundsBuilder.build(), 100) ) } PolyUtil.decode из maps-utils раскодирует Encoded Polyline.
Почему Яндекс MapKit лучше для офлайн-режима?
Яндекс строит маршруты прямо в SDK без REST-вызовов. Это даёт офлайн-работу при использовании Full SDK с загруженными картами. Кроме того, SDK возвращает мета-информацию: дистанция, время с учётом пробок, платные участки.
Пример кода для Яндекс MapKit
```kotlin val drivingRouter = DirectionsFactory.getInstance() .createDrivingRouter(DrivingRouterType.COMBINED)val routePoints = listOf( RequestPoint(Point(55.7558, 37.6173), RequestPointType.WAYPOINT, null, null), RequestPoint(Point(59.9343, 30.3351), RequestPointType.WAYPOINT, null, null) )
val drivingSession = drivingRouter.requestRoutes( routePoints, DrivingOptions().apply { routesCount = 3 // запросить несколько вариантов avoidTolls = false avoidPoorConditions = true }, VehicleOptions(), object : DrivingSession.DrivingRouteListener { override fun onDrivingRoutes(routes: List<DrivingRoute>) { routes.forEachIndexed { index, route -> val color = if (index == 0) Color.BLUE else Color.GRAY val polyline = mapObjectCollection.addPolyline(route.geometry).apply { strokeColor = color strokeWidth = if (index == 0) 6f else 3f zIndex = if (index == 0) 1f else 0f } // Клик по альтернативному маршруту polyline.addTapListener { _, _ -> selectRoute(index) true } }
// Мета-информация первого маршрута routes.firstOrNull()?.let { route -> val metadata = route.metadata.weight val distance = metadata.distance.text // "350 км" val time = metadata.timeWithTraffic.text // "4 ч 20 мин" showRouteInfo(distance, time) } } override fun onDrivingRoutesError(error: Error) {} } )
</details> #### Пешеходный и транзитный маршруты Вместо `DrivingRouter` — `PedestrianRouter` или `TransitRouter`. Транзитный маршрут возвращает список сегментов: пешие участки, автобусы, метро — с временем и остановками. ### Как реализовать построение маршрутов на iOS с MapKit? ```swift func buildDrivingRoute(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) { let request = MKDirections.Request() request.source = MKMapItem(placemark: MKPlacemark(coordinate: from)) request.destination = MKMapItem(placemark: MKPlacemark(coordinate: to)) request.transportType = .automobile request.requestsAlternateRoutes = true MKDirections(request: request).calculate { [weak self] response, error in guard let routes = response?.routes, !routes.isEmpty else { return } // Рисуем все маршруты, первый — основной routes.enumerated().forEach { index, route in let renderer = MKPolylineRenderer(polyline: route.polyline) renderer.strokeColor = index == 0 ? .systemBlue : .systemGray renderer.lineWidth = index == 0 ? 5 : 3 self?.mapView.addOverlay(route.polyline, level: .aboveRoads) } // Зум на маршрут self?.mapView.setVisibleMapRect( routes[0].polyline.boundingMapRect, edgePadding: UIEdgeInsets(top: 60, left: 40, bottom: 80, right: 40), animated: true ) } } Пошаговая навигация и маневры
Для пошаговой навигации используйте MKDirections.Shared. После расчета маршрута получите массив MKDirections.TransportType. Для отображения маневров можно использовать MKMapItem с указанием поворотов. Свяжитесь с нами для получения полного примера.
Промежуточные точки (Waypoints)
| Провайдер | Max waypoints | Ограничения |
|---|---|---|
| Google Directions API | 23 (с оптимизацией) | Бесплатно до 2500 запросов/день |
| Яндекс MapKit | 50 | Поддерживает VIAPOINT и WAYPOINT |
| Apple MKDirections | 10 | Только на iOS 16+ |
| Mapbox Directions | 25 | В зависимости от тарифа |
| 2ГИС SDK | 30 | В офлайн-режиме до 10 |
В приложениях курьерских служб часто требуется 5–10 промежуточных точек — это решаемо. Все провайдеры поддерживают waypoints: в Google Directions API — параметр &waypoints=lat,lng|lat,lng, в Яндекс MapKit — добавить RequestPoint с типом VIAPOINT, в MKDirections — через waypoints (iOS 16+).
Объём работ по интеграции маршрутов
- Интеграция выбранного SDK (Google, Яндекс, Mapbox, 2ГИС) с настройкой API-ключей и provisioning profiles.
- Реализация построения маршрутов для 1–3 типов транспорта (авто, пешком, транспорт).
- Отрисовка полилиний с кастомными стилями, альтернативные маршруты, пересчёт при отклонении.
- Поддержка waypoints и пошаговой навигации (маневры).
- Документация по интеграции и рекомендации по оптимизации.
- Консультации по публикации в App Store (App Review Guidelines раздел 4.2/5.1) и Google Play.
Получите консультацию по оценке сложности вашего проекта.
Распространённые проблемы интеграции
Маршрут не перестраивается при изменении точек. Старый Polyline не удалён перед добавлением нового. Нужно хранить ссылку на текущий overlay и удалять его: mapView.removeOverlay(currentRoute).
Encoding polyline не декодируется. Google использует точность 1e5 (5 знаков после запятой). Яндекс — собственный формат. Не используйте универсальный decoder для Яндекс-маршрутов.
Сроки
Один тип маршрута без waypoints — 1 день. Несколько режимов транспорта, альтернативные маршруты и пересчёт — 2–3 дня. Стоимость рассчитывается индивидуально.
Наш опыт: 7+ лет в мобильной разработке, более 40 проектов с картами. Закажите оценку вашего проекта — мы подберём оптимальное решение.







