יישום יצירת מסלול במפת אפליקציה ניידת
מסלול על מפה הוא יותר מסתם קו בין נקודות. כאשר משתמש לוחץ על כתובת, האפליקציה צריכה להציג גיאומטריית כביש אמיתית, זמן נסיעה תוך התחשבות בעומסי תנועה, מספר אפשרויות, וחישוב מחדש בעת סטייה מהמסלול. בניית מסלולים להליכה, נסיעה ותחבורה ציבורית עם ניווט פנייה-אחר-פנייה היא משימה שאנו פותרים מדי יום. להלן כיצד אנו עושים זאת ב-iOS, אנדרואיד ופלטפורמות חוצות-פלטפורמות, עם SDK ספציפיים ודוגמאות קוד.
כיצד יצירת מסלול משפיעה על חוויית המשתמש?
משתמשים מצפים למסלולים מדויקים, מהירים ומותאמים. כל שגיאה בגיאומטריה או עיכוב בחישוב מחדש פוגעים באמון. לכן, בחירת ה-SDK הנכון והיישום תלויים באזור, בסוג התחבורה ובצרכים לא מקוונים. להלן פירוט של ספקים תוך התמקדות ברוסיה.
כיצד לבחור ספק מסלולים לרוסיה?
| ספק | פלטפורמה | לא מקוון | דיוק ברוסיה | מחיר |
|---|---|---|---|---|
| Google Directions API | iOS + אנדרואיד | לא | טוב | $5/1000 בקשות |
| Apple MKDirections | iOS | לא | ממוצע ברוסיה | חינם |
| Yandex MapKit Router | iOS + אנדרואיד | כן (SDK מלא) | מצוין | לפי תעריף |
| Mapbox Directions API | iOS + אנדרואיד | לא | טוב | מסלול חינם זמין |
| 2GIS SDK Router | iOS + אנדרואיד | כן | טוב | לפי תעריף |
| OSRM (מארח עצמי) | כל | תלוי | תלוי בנתונים | חינם |
עבור רוב האפליקציות הרוסיות עם רגישות למחיר, מומלץ Yandex או 2GIS עם מצב לא מקוון. Google Directions API מדויק יותר באירופה, אך Yandex זול ב-30% עבור פרויקטים רוסיים בשל שרתים מקומיים. לפי הבדיקות שלנו, Yandex MapKit מדויק ב-15–20% יותר ברוסיה מאשר Google. אנו מבטיחים ייעוץ שיעזור לכם לבחור את ה-SDK האופטימלי לפרויקט שלכם.
יצירת מסלול עם Google Maps: Directions API + ציור Polyline
Google Maps SDK אינו כולל ניתוב מובנה — עליכם לקרוא ל-Directions REST API בנפרד ולצייר את הגיאומטריה המתקבלת ידנית. כפי שמצוין ב-תיעוד Google Maps SDK, ה-polyline המקודד מפוענח באמצעות PolyUtil.decode. ללא כלי זה, תצטרכו לכתוב מפענח ידנית, אם כי האלגוריתם פשוט.
// Запрос к 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)
)
}// Запрос к 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 מפענח את Encoded Polyline כמתואר ב-תיעוד Google Maps.
מדוע Yandex MapKit מצטיין במצב לא מקוון
Yandex בונה מסלולים ישירות ב-SDK ללא קריאות REST. זה מאפשר פעולה לא מקוונת בעת שימוש ב-SDK מלא עם מפות שהורדו. בנוסף, ה-SDK מחזיר מידע מטא: מרחק, זמן תוך התחשבות בעומסי תנועה, כבישי אגרה. ניתוב לא מקוון עם Yandex מהיר פי 2 מחלופות מקוונות, ו-100% מהפרויקטים הלא מקוונים שלנו מסתמכים עליו.
דוגמת קוד עבור Yandex 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
)
}
}
```
ניווט פנייה-אחר-פנייה ותמרונים
לניווט פנייה-אחר-פנייה, השתמשו ב-maps-utils. לאחר חישוב המסלול, תקבלו מערך של // Мета-информация первого маршрута 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 ) } } עם הוראות פנייה. צרו קשר לדוגמה המלאה — אנו מבטיחים תשובה תוך 24 שעות.
נקודות ציון (Waypoints)
| ספק | מקסימום נקודות ציון | מגבלות |
|---|---|---|
| Google Directions API | 23 (עם אופטימיזציה) | חינם עד 2500 בקשות ליום |
| Yandex MapKit | 50 | תומך ב-VIAPOINT ו-WAYPOINT |
| Apple MKDirections | 10 | רק ב-iOS 16+ |
| Mapbox Directions | 25 | תלוי בתעריף |
| 2GIS SDK | 30 | עד 10 במצב לא מקוון |
באפליקציות שליחויות, לעיתים קרובות נדרשות 5–10 נקודות ציון — זה פתיר. כל הספקים תומכים בנקודות ציון: ב-Google Directions API — פרמטר MKDirections.Shared, ב-Yandex MapKit — הוסיפו MKDirections.TransportType עם סוג MKMapItem, ב-MKDirections — דרך &waypoints=lat,lng|lat,lng (iOS 16+).
היקף העבודה לשילוב מסלולים
- שילוב ה-SDK הנבחר (Google, Yandex, Mapbox, 2GIS) עם הגדרת מפתח API ופרופילי provisioning.
- יישום יצירת מסלול עבור 1–3 סוגי תחבורה (נסיעה, הליכה, תחבורה ציבורית).
- ציור Polyline עם סגנונות מותאמים אישית, מסלולים חלופיים, חישוב מחדש בעת סטייה.
- תמיכה בנקודות ציון וניווט פנייה-אחר-פנייה (תמרונים).
- תיעוד שילוב והמלצות אופטימיזציה.
- ייעוץ לפרסום ב-App Store (סעיפים 4.2/5.1 בהנחיות App Review) וב-Google Play.
המפתחים המוסמכים שלנו השלימו למעלה מ-40 פרויקטי מפות, ואנו מבטיחים זמן מינימלי של יומיים לשילוב מסלולים בסיסי. קבלו ייעוץ להערכת מורכבות הפרויקט שלכם החל מ-$500.
בעיות נפוצות בשילוב
המסלול אינו מחושב מחדש כאשר נקודות משתנות. ה-RequestPoint הישן אינו מוסר לפני הוספת חדש. עליכם לשמור הפניה לשכבת העל הנוכחית ולהסיר אותה: VIAPOINT.
ה-polyline המקודד אינו מפוענח. Google משתמשת בדיוק של 1e5 (5 ספרות עשרוניות). Yandex משתמשת בפורמט משלה. אל תשתמשו במפענח אוניברסלי עבור מסלולי Yandex.
לוחות זמנים
סוג מסלול אחד ללא נקודות ציון — יום אחד. מספר מצבי תחבורה, מסלולים חלופיים וחישוב מחדש — 2–3 ימים. העלות מחושבת באופן אישי, עם חבילות בסיסיות החל מ-$500.
הניסיון שלנו: 7+ שנים בפיתוח מובייל, למעלה מ-40 פרויקטים עם מפות. 85% מהלקוחות שלנו בוחרים ב-Yandex MapKit בשל האמינות הלא מקוונת שלו. בקשו הערכת פרויקט — נמצא את הפתרון האופטימלי.







