@sargsyansevak/expo-mapbox-navigation 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +313 -0
- package/android/build.gradle +98 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationModule.kt +120 -0
- package/android/src/main/java/expo/modules/mapboxnavigation/ExpoMapboxNavigationView.kt +1100 -0
- package/android/src/main/res/drawable-hdpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-hdpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-hdpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-hdpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-hdpi/icon_x.png +0 -0
- package/android/src/main/res/drawable-ldpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-ldpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-ldpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-ldpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-ldpi/icon_x.png +0 -0
- package/android/src/main/res/drawable-mdpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-mdpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-mdpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-mdpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-mdpi/icon_x.png +0 -0
- package/android/src/main/res/drawable-xhdpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-xhdpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-xhdpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-xhdpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-xhdpi/icon_x.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-xxhdpi/icon_x.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/icon_compass.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/icon_mute.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/icon_overview.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/icon_sound.png +0 -0
- package/android/src/main/res/drawable-xxxhdpi/icon_x.png +0 -0
- package/android/src/main/res/values/styles.xml +9 -0
- package/app.plugin.js +1 -0
- package/build/ExpoMapboxNavigation.types.d.ts +90 -0
- package/build/ExpoMapboxNavigation.types.d.ts.map +1 -0
- package/build/ExpoMapboxNavigation.types.js +2 -0
- package/build/ExpoMapboxNavigation.types.js.map +1 -0
- package/build/ExpoMapboxNavigationModule.d.ts +3 -0
- package/build/ExpoMapboxNavigationModule.d.ts.map +1 -0
- package/build/ExpoMapboxNavigationModule.js +3 -0
- package/build/ExpoMapboxNavigationModule.js.map +1 -0
- package/build/ExpoMapboxNavigationView.d.ts +5 -0
- package/build/ExpoMapboxNavigationView.d.ts.map +1 -0
- package/build/ExpoMapboxNavigationView.js +7 -0
- package/build/ExpoMapboxNavigationView.js.map +1 -0
- package/build/ExpoMapboxNavigationView.web.d.ts +4 -0
- package/build/ExpoMapboxNavigationView.web.d.ts.map +1 -0
- package/build/ExpoMapboxNavigationView.web.js +7 -0
- package/build/ExpoMapboxNavigationView.web.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/ExpoMapboxNavigation.podspec +46 -0
- package/ios/ExpoMapboxNavigationModule.swift +92 -0
- package/ios/ExpoMapboxNavigationView.swift +509 -0
- package/package.json +47 -0
- package/plugin/config.js +127 -0
- package/src/ExpoMapboxNavigation.types.ts +70 -0
- package/src/ExpoMapboxNavigationModule.ts +2 -0
- package/src/ExpoMapboxNavigationView.tsx +17 -0
- package/src/ExpoMapboxNavigationView.web.tsx +13 -0
- package/src/index.ts +11 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import MapboxNavigationCore
|
|
3
|
+
import MapboxMaps
|
|
4
|
+
import MapboxNavigationUIKit
|
|
5
|
+
import MapboxDirections
|
|
6
|
+
import Combine
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ExpoMapboxNavigationView: ExpoView {
|
|
10
|
+
private let onRouteProgressChanged = EventDispatcher()
|
|
11
|
+
private let onCancelNavigation = EventDispatcher()
|
|
12
|
+
private let onWaypointArrival = EventDispatcher()
|
|
13
|
+
private let onFinalDestinationArrival = EventDispatcher()
|
|
14
|
+
private let onRouteChanged = EventDispatcher()
|
|
15
|
+
private let onUserOffRoute = EventDispatcher()
|
|
16
|
+
private let onRoutesLoaded = EventDispatcher()
|
|
17
|
+
private let onRouteFailedToLoad = EventDispatcher()
|
|
18
|
+
|
|
19
|
+
let controller = ExpoMapboxNavigationViewController()
|
|
20
|
+
|
|
21
|
+
required init(appContext: AppContext? = nil) {
|
|
22
|
+
super.init(appContext: appContext)
|
|
23
|
+
clipsToBounds = true
|
|
24
|
+
addSubview(controller.view)
|
|
25
|
+
|
|
26
|
+
controller.onRouteProgressChanged = onRouteProgressChanged
|
|
27
|
+
controller.onCancelNavigation = onCancelNavigation
|
|
28
|
+
controller.onWaypointArrival = onWaypointArrival
|
|
29
|
+
controller.onFinalDestinationArrival = onFinalDestinationArrival
|
|
30
|
+
controller.onRouteChanged = onRouteChanged
|
|
31
|
+
controller.onUserOffRoute = onUserOffRoute
|
|
32
|
+
controller.onRoutesLoaded = onRoutesLoaded
|
|
33
|
+
controller.onRouteFailedToLoad = onRouteFailedToLoad
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
override func layoutSubviews() {
|
|
37
|
+
controller.view.frame = bounds
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ExpoMapboxNavigationViewController: UIViewController {
|
|
43
|
+
static let navigationProvider: MapboxNavigationProvider = MapboxNavigationProvider(coreConfig: CoreConfig(routingConfig: RoutingConfig(fasterRouteDetectionConfig: Optional<FasterRouteDetectionConfig>.none),locationSource: .live ))
|
|
44
|
+
var mapboxNavigation: MapboxNavigation? = nil
|
|
45
|
+
var routingProvider: RoutingProvider? = nil
|
|
46
|
+
var navigation: NavigationController? = nil
|
|
47
|
+
var tripSession: SessionController? = nil
|
|
48
|
+
var navigationViewController: NavigationViewController? = nil
|
|
49
|
+
|
|
50
|
+
var currentCoordinates: Array<CLLocationCoordinate2D>? = nil
|
|
51
|
+
var initialLocation: CLLocationCoordinate2D? = nil
|
|
52
|
+
var initialLocationZoom: Double? = nil
|
|
53
|
+
var currentWaypointIndices: Array<Int>? = nil
|
|
54
|
+
var currentLocale: Locale = Locale.current
|
|
55
|
+
var currentRouteProfile: String? = nil
|
|
56
|
+
var currentRouteExcludeList: Array<String>? = nil
|
|
57
|
+
var currentMapStyle: String? = nil
|
|
58
|
+
var currentCustomRasterSourceUrl: String? = nil
|
|
59
|
+
var currentPlaceCustomRasterLayerAbove: String? = nil
|
|
60
|
+
var currentDisableAlternativeRoutes: Bool? = nil
|
|
61
|
+
var currentFollowingZoom: Double? = nil
|
|
62
|
+
var isUsingRouteMatchingApi: Bool = false
|
|
63
|
+
var vehicleMaxHeight: Double? = nil
|
|
64
|
+
var vehicleMaxWidth: Double? = nil
|
|
65
|
+
|
|
66
|
+
var onRouteProgressChanged: EventDispatcher? = nil
|
|
67
|
+
var onCancelNavigation: EventDispatcher? = nil
|
|
68
|
+
var onWaypointArrival: EventDispatcher? = nil
|
|
69
|
+
var onFinalDestinationArrival: EventDispatcher? = nil
|
|
70
|
+
var onRouteChanged: EventDispatcher? = nil
|
|
71
|
+
var onUserOffRoute: EventDispatcher? = nil
|
|
72
|
+
var onRoutesLoaded: EventDispatcher? = nil
|
|
73
|
+
var onRouteFailedToLoad: EventDispatcher? = nil
|
|
74
|
+
|
|
75
|
+
var calculateRoutesTask: Task<Void, Error>? = nil
|
|
76
|
+
private var routeProgressCancellable: AnyCancellable? = nil
|
|
77
|
+
private var waypointArrivalCancellable: AnyCancellable? = nil
|
|
78
|
+
private var reroutingCancellable: AnyCancellable? = nil
|
|
79
|
+
private var sessionCancellable: AnyCancellable? = nil
|
|
80
|
+
|
|
81
|
+
init() {
|
|
82
|
+
super.init(nibName: nil, bundle: nil)
|
|
83
|
+
mapboxNavigation = ExpoMapboxNavigationViewController.navigationProvider.mapboxNavigation
|
|
84
|
+
routingProvider = mapboxNavigation!.routingProvider()
|
|
85
|
+
navigation = mapboxNavigation!.navigation()
|
|
86
|
+
tripSession = mapboxNavigation!.tripSession()
|
|
87
|
+
|
|
88
|
+
routeProgressCancellable = navigation!.routeProgress.sink { progressState in
|
|
89
|
+
if(progressState != nil){
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
// For some reason the maneuver arrows sometimes (not consistently) show up the same color as the route line making them invisible.
|
|
93
|
+
// This is a hack to always ensure the arrows are visible.
|
|
94
|
+
try? self.navigationViewController?.navigationMapView?.mapView.mapboxMap.setLayerProperty(
|
|
95
|
+
for: "com.mapbox.navigation.arrow.next",
|
|
96
|
+
property: "line-color",
|
|
97
|
+
value: "#FFFFFF"
|
|
98
|
+
)
|
|
99
|
+
try? self.navigationViewController?.navigationMapView?.mapView.mapboxMap.setLayerProperty(
|
|
100
|
+
for: "com.mapbox.navigation.arrow.next.stroke",
|
|
101
|
+
property: "line-color",
|
|
102
|
+
value: "#FFFFFF"
|
|
103
|
+
)
|
|
104
|
+
try? self.navigationViewController?.navigationMapView?.mapView.mapboxMap.setLayerProperty(
|
|
105
|
+
for: "com.mapbox.navigation.arrow.next.symbol",
|
|
106
|
+
property: "icon-color",
|
|
107
|
+
value: "#FFFFFF"
|
|
108
|
+
)
|
|
109
|
+
try? self.navigationViewController?.navigationMapView?.mapView.mapboxMap.setLayerProperty(
|
|
110
|
+
for: "com.mapbox.navigation.arrow.next.symbol.casing",
|
|
111
|
+
property: "icon-color",
|
|
112
|
+
value: "#FFFFFF"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
self.onRouteProgressChanged?([
|
|
116
|
+
"distanceRemaining": progressState!.routeProgress.distanceRemaining,
|
|
117
|
+
"distanceTraveled": progressState!.routeProgress.distanceTraveled,
|
|
118
|
+
"durationRemaining": progressState!.routeProgress.durationRemaining,
|
|
119
|
+
"fractionTraveled": progressState!.routeProgress.fractionTraveled,
|
|
120
|
+
])
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
waypointArrivalCancellable = navigation!.waypointsArrival.sink { arrivalStatus in
|
|
125
|
+
let event = arrivalStatus.event
|
|
126
|
+
if event is WaypointArrivalStatus.Events.ToFinalDestination {
|
|
127
|
+
self.onFinalDestinationArrival?()
|
|
128
|
+
} else if event is WaypointArrivalStatus.Events.ToWaypoint {
|
|
129
|
+
self.onWaypointArrival?()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
reroutingCancellable = navigation!.rerouting.sink { rerouteStatus in
|
|
134
|
+
self.onRouteChanged?()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
sessionCancellable = tripSession!.session.sink { session in
|
|
138
|
+
let state = session.state
|
|
139
|
+
switch state {
|
|
140
|
+
case .activeGuidance(let activeGuidanceState):
|
|
141
|
+
switch(activeGuidanceState){
|
|
142
|
+
case .offRoute:
|
|
143
|
+
self.onUserOffRoute?()
|
|
144
|
+
default: break
|
|
145
|
+
}
|
|
146
|
+
default: break
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
deinit {
|
|
153
|
+
routeProgressCancellable?.cancel()
|
|
154
|
+
waypointArrivalCancellable?.cancel()
|
|
155
|
+
reroutingCancellable?.cancel()
|
|
156
|
+
sessionCancellable?.cancel()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
override func viewDidDisappear(_ animated: Bool) {
|
|
160
|
+
super.viewDidDisappear(animated)
|
|
161
|
+
Task { @MainActor in tripSession?.setToIdle() } // Stops navigation
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
required init?(coder aDecoder: NSCoder) {
|
|
165
|
+
super.init(coder: aDecoder)
|
|
166
|
+
fatalError("This controller should not be loaded through a story board")
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
func addCustomRasterLayer() {
|
|
170
|
+
let navigationMapView = navigationViewController?.navigationMapView
|
|
171
|
+
let sourceId = "raster-source"
|
|
172
|
+
let layerId = "raster-layer"
|
|
173
|
+
|
|
174
|
+
if(currentCustomRasterSourceUrl == nil){
|
|
175
|
+
if let mapView = navigationMapView?.mapView.mapboxMap {
|
|
176
|
+
if mapView.layerExists(withId: layerId) {
|
|
177
|
+
try? mapView.removeLayer(withId: layerId)
|
|
178
|
+
}
|
|
179
|
+
if mapView.sourceExists(withId: sourceId) {
|
|
180
|
+
try? mapView.removeSource(withId: sourceId)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let sourceUrl = currentCustomRasterSourceUrl!
|
|
187
|
+
|
|
188
|
+
var rasterSource = RasterSource(id: sourceId)
|
|
189
|
+
|
|
190
|
+
rasterSource.tiles = [sourceUrl]
|
|
191
|
+
rasterSource.tileSize = 256
|
|
192
|
+
|
|
193
|
+
let rasterLayer = RasterLayer(id: layerId, source: sourceId)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
if let mapView = navigationMapView?.mapView.mapboxMap {
|
|
197
|
+
if mapView.layerExists(withId: layerId) {
|
|
198
|
+
try? mapView.removeLayer(withId: layerId)
|
|
199
|
+
}
|
|
200
|
+
if mapView.sourceExists(withId: sourceId) {
|
|
201
|
+
try? mapView.removeSource(withId: sourceId)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
try? mapView.addSource(rasterSource)
|
|
205
|
+
try? mapView.addLayer(rasterLayer, layerPosition: .above(currentPlaceCustomRasterLayerAbove ?? "water"))
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
func setCoordinates(coordinates: Array<CLLocationCoordinate2D>) {
|
|
211
|
+
currentCoordinates = coordinates
|
|
212
|
+
update()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
func setVehicleMaxHeight(maxHeight: Double?) {
|
|
216
|
+
vehicleMaxHeight = maxHeight
|
|
217
|
+
update()
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
func setVehicleMaxWidth(maxWidth: Double?) {
|
|
221
|
+
vehicleMaxWidth = maxWidth
|
|
222
|
+
update()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
func setLocale(locale: String?) {
|
|
226
|
+
if(locale != nil){
|
|
227
|
+
currentLocale = Locale(identifier: locale!)
|
|
228
|
+
} else {
|
|
229
|
+
currentLocale = Locale.current
|
|
230
|
+
}
|
|
231
|
+
update()
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
func setIsUsingRouteMatchingApi(useRouteMatchingApi: Bool?){
|
|
235
|
+
isUsingRouteMatchingApi = useRouteMatchingApi ?? false
|
|
236
|
+
update()
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
func setWaypointIndices(waypointIndices: Array<Int>?){
|
|
240
|
+
currentWaypointIndices = waypointIndices
|
|
241
|
+
update()
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
func setRouteProfile(profile: String?){
|
|
245
|
+
currentRouteProfile = profile
|
|
246
|
+
update()
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
func setRouteExcludeList(excludeList: Array<String>?){
|
|
250
|
+
currentRouteExcludeList = excludeList
|
|
251
|
+
update()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
func setMapStyle(style: String?){
|
|
255
|
+
currentMapStyle = style
|
|
256
|
+
update()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
func setCustomRasterSourceUrl(url: String?){
|
|
260
|
+
currentCustomRasterSourceUrl = url
|
|
261
|
+
update()
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
func setPlaceCustomRasterLayerAbove(layerId: String?){
|
|
265
|
+
currentPlaceCustomRasterLayerAbove = layerId
|
|
266
|
+
update()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
func setDisableAlternativeRoutes(disableAlternativeRoutes: Bool?){
|
|
270
|
+
currentDisableAlternativeRoutes = disableAlternativeRoutes
|
|
271
|
+
update()
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
func recenterMap(){
|
|
275
|
+
let navigationMapView = navigationViewController?.navigationMapView
|
|
276
|
+
navigationMapView?.navigationCamera.update(cameraState: .following)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
func setIsMuted(isMuted: Bool?){
|
|
280
|
+
if(isMuted != nil){
|
|
281
|
+
ExpoMapboxNavigationViewController.navigationProvider.routeVoiceController.speechSynthesizer.muted = isMuted!
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
func setInitialLocation(location: CLLocationCoordinate2D, zoom: Double?){
|
|
286
|
+
initialLocation = location
|
|
287
|
+
initialLocationZoom = zoom
|
|
288
|
+
let navigationMapView = navigationViewController?.navigationMapView
|
|
289
|
+
if(initialLocation != nil && navigationMapView != nil){
|
|
290
|
+
navigationMapView!.mapView.mapboxMap.setCamera(to: CameraOptions(center: initialLocation!, zoom: initialLocationZoom ?? 15))
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
func setFollowingZoom(followingZoom: Double?){
|
|
295
|
+
let navigationMapView = navigationViewController?.navigationMapView
|
|
296
|
+
currentFollowingZoom = followingZoom
|
|
297
|
+
if(navigationMapView != nil && followingZoom != nil){
|
|
298
|
+
let newDataSource = MobileViewportDataSource(navigationMapView!.mapView)
|
|
299
|
+
newDataSource.options.followingCameraOptions.zoomRange = followingZoom!...followingZoom!
|
|
300
|
+
navigationMapView?.navigationCamera.viewportDataSource = newDataSource
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
func update(){
|
|
305
|
+
calculateRoutesTask?.cancel()
|
|
306
|
+
|
|
307
|
+
if(currentCoordinates != nil){
|
|
308
|
+
let waypoints = currentCoordinates!.enumerated().map {
|
|
309
|
+
let index = $0
|
|
310
|
+
let coordinate = $1
|
|
311
|
+
var waypoint = Waypoint(coordinate: coordinate)
|
|
312
|
+
waypoint.separatesLegs = currentWaypointIndices == nil ? true : currentWaypointIndices!.contains(index)
|
|
313
|
+
return waypoint
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if(isUsingRouteMatchingApi){
|
|
317
|
+
calculateMapMatchingRoutes(waypoints: waypoints)
|
|
318
|
+
} else {
|
|
319
|
+
calculateRoutes(waypoints: waypoints)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
func calculateRoutes(waypoints: Array<Waypoint>){
|
|
325
|
+
let routeOptions = NavigationRouteOptions(
|
|
326
|
+
waypoints: waypoints,
|
|
327
|
+
profileIdentifier: currentRouteProfile != nil ? ProfileIdentifier(rawValue: currentRouteProfile!) : nil,
|
|
328
|
+
queryItems: [
|
|
329
|
+
URLQueryItem(name: "exclude", value: currentRouteExcludeList?.joined(separator: ",")),
|
|
330
|
+
URLQueryItem(name: "max_height", value: String(format: "%.1f", vehicleMaxHeight ?? 0.0)),
|
|
331
|
+
URLQueryItem(name: "max_width", value: String(format: "%.1f", vehicleMaxWidth ?? 0.0))
|
|
332
|
+
],
|
|
333
|
+
locale: currentLocale,
|
|
334
|
+
distanceUnit: currentLocale.usesMetricSystem ? LengthFormatter.Unit.meter : LengthFormatter.Unit.mile
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
calculateRoutesTask = Task {
|
|
338
|
+
switch await self.routingProvider!.calculateRoutes(options: routeOptions).result {
|
|
339
|
+
case .failure(let error):
|
|
340
|
+
onRouteFailedToLoad?([
|
|
341
|
+
"errorMessage": error.localizedDescription
|
|
342
|
+
])
|
|
343
|
+
print(error.localizedDescription)
|
|
344
|
+
case .success(let navigationRoutes):
|
|
345
|
+
onRoutesCalculated(navigationRoutes: navigationRoutes)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
func calculateMapMatchingRoutes(waypoints: Array<Waypoint>){
|
|
351
|
+
let matchOptions = NavigationMatchOptions(
|
|
352
|
+
waypoints: waypoints,
|
|
353
|
+
profileIdentifier: currentRouteProfile != nil ? ProfileIdentifier(rawValue: currentRouteProfile!) : nil,
|
|
354
|
+
queryItems: [URLQueryItem(name: "exclude", value: currentRouteExcludeList?.joined(separator: ","))],
|
|
355
|
+
distanceUnit: currentLocale.usesMetricSystem ? LengthFormatter.Unit.meter : LengthFormatter.Unit.mile
|
|
356
|
+
)
|
|
357
|
+
matchOptions.locale = currentLocale
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
calculateRoutesTask = Task {
|
|
361
|
+
switch await self.routingProvider!.calculateRoutes(options: matchOptions).result {
|
|
362
|
+
case .failure(let error):
|
|
363
|
+
onRouteFailedToLoad?([
|
|
364
|
+
"errorMessage": error.localizedDescription
|
|
365
|
+
])
|
|
366
|
+
print(error.localizedDescription)
|
|
367
|
+
case .success(let navigationRoutes):
|
|
368
|
+
onRoutesCalculated(navigationRoutes: navigationRoutes)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
@objc func cancelButtonClicked(_ sender: AnyObject?) {
|
|
374
|
+
onCancelNavigation?()
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
func convertRoute(route: Route) -> Any {
|
|
378
|
+
return [
|
|
379
|
+
"distance": route.distance,
|
|
380
|
+
"expectedTravelTime": route.expectedTravelTime,
|
|
381
|
+
"legs": route.legs.map { leg in
|
|
382
|
+
return [
|
|
383
|
+
"source": leg.source != nil ? [
|
|
384
|
+
"latitude": leg.source!.coordinate.latitude,
|
|
385
|
+
"longitude": leg.source!.coordinate.longitude
|
|
386
|
+
] : nil,
|
|
387
|
+
"destination": leg.destination != nil ? [
|
|
388
|
+
"latitude": leg.destination!.coordinate.latitude,
|
|
389
|
+
"longitude": leg.destination!.coordinate.longitude
|
|
390
|
+
] : nil,
|
|
391
|
+
"steps": leg.steps.map { step in
|
|
392
|
+
return [
|
|
393
|
+
"shape": step.shape != nil ? [
|
|
394
|
+
"coordinates": step.shape!.coordinates.map { coordinate in
|
|
395
|
+
return [
|
|
396
|
+
"latitude": coordinate.latitude,
|
|
397
|
+
"longitude": coordinate.longitude,
|
|
398
|
+
]
|
|
399
|
+
}
|
|
400
|
+
] : nil
|
|
401
|
+
]
|
|
402
|
+
}
|
|
403
|
+
]
|
|
404
|
+
}
|
|
405
|
+
]
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
func onRoutesCalculated(navigationRoutes: NavigationRoutes){
|
|
409
|
+
onRoutesLoaded?([
|
|
410
|
+
"routes": [
|
|
411
|
+
"mainRoute": convertRoute(route: navigationRoutes.mainRoute.route),
|
|
412
|
+
"alternativeRoutes": navigationRoutes.alternativeRoutes.map { convertRoute(route: $0.route) }
|
|
413
|
+
]
|
|
414
|
+
])
|
|
415
|
+
|
|
416
|
+
let topBanner = TopBannerViewController()
|
|
417
|
+
topBanner.instructionsBannerView.distanceFormatter.locale = currentLocale
|
|
418
|
+
let bottomBanner = BottomBannerViewController()
|
|
419
|
+
bottomBanner.distanceFormatter.locale = currentLocale
|
|
420
|
+
bottomBanner.dateFormatter.locale = currentLocale
|
|
421
|
+
|
|
422
|
+
let navigationOptions = NavigationOptions(
|
|
423
|
+
mapboxNavigation: self.mapboxNavigation!,
|
|
424
|
+
voiceController: ExpoMapboxNavigationViewController.navigationProvider.routeVoiceController,
|
|
425
|
+
eventsManager: ExpoMapboxNavigationViewController.navigationProvider.eventsManager(),
|
|
426
|
+
styles: [DayStyle()],
|
|
427
|
+
topBanner: topBanner,
|
|
428
|
+
bottomBanner: bottomBanner
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
let newNavigationControllerRequired = navigationViewController == nil
|
|
432
|
+
|
|
433
|
+
if(newNavigationControllerRequired){
|
|
434
|
+
navigationViewController = NavigationViewController(
|
|
435
|
+
navigationRoutes: navigationRoutes,
|
|
436
|
+
navigationOptions: navigationOptions
|
|
437
|
+
)
|
|
438
|
+
} else {
|
|
439
|
+
navigationViewController!.prepareViewLoading(
|
|
440
|
+
navigationRoutes: navigationRoutes,
|
|
441
|
+
navigationOptions: navigationOptions
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
let navigationViewController = navigationViewController!
|
|
446
|
+
|
|
447
|
+
navigationViewController.showsContinuousAlternatives = currentDisableAlternativeRoutes != true
|
|
448
|
+
navigationViewController.usesNightStyleWhileInTunnel = false
|
|
449
|
+
navigationViewController.automaticallyAdjustsStyleForTimeOfDay = false
|
|
450
|
+
|
|
451
|
+
let navigationMapView = navigationViewController.navigationMapView
|
|
452
|
+
navigationMapView!.puckType = .puck2D(.navigationDefault)
|
|
453
|
+
|
|
454
|
+
if(initialLocation != nil && newNavigationControllerRequired){
|
|
455
|
+
navigationMapView!.mapView.mapboxMap.setCamera(to: CameraOptions(center: initialLocation!, zoom: initialLocationZoom ?? 15))
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
let style = currentMapStyle != nil ? StyleURI(rawValue: currentMapStyle!) : StyleURI.streets
|
|
459
|
+
navigationMapView!.mapView.mapboxMap.loadStyle(style!, completion: { _ in
|
|
460
|
+
navigationMapView!.localizeLabels(locale: self.currentLocale)
|
|
461
|
+
do{
|
|
462
|
+
try navigationMapView!.mapView.mapboxMap.localizeLabels(into: self.currentLocale)
|
|
463
|
+
} catch {}
|
|
464
|
+
self.addCustomRasterLayer()
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
let cancelButton = navigationViewController.navigationView.bottomBannerContainerView.findViews(subclassOf: CancelButton.self)[0]
|
|
469
|
+
cancelButton.addTarget(self, action: #selector(cancelButtonClicked), for: .touchUpInside)
|
|
470
|
+
|
|
471
|
+
navigationViewController.delegate = self
|
|
472
|
+
addChild(navigationViewController)
|
|
473
|
+
view.addSubview(navigationViewController.view)
|
|
474
|
+
navigationViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
475
|
+
NSLayoutConstraint.activate([
|
|
476
|
+
navigationViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
|
|
477
|
+
navigationViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0),
|
|
478
|
+
navigationViewController.view.topAnchor.constraint(equalTo: view.topAnchor, constant: 0),
|
|
479
|
+
navigationViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0),
|
|
480
|
+
])
|
|
481
|
+
didMove(toParent: self)
|
|
482
|
+
mapboxNavigation!.tripSession().startActiveGuidance(with: navigationRoutes, startLegIndex: 0)
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
extension ExpoMapboxNavigationViewController: NavigationViewControllerDelegate {
|
|
486
|
+
func navigationViewController(_ navigationViewController: NavigationViewController, didRerouteAlong route: Route) {
|
|
487
|
+
onRoutesLoaded?([
|
|
488
|
+
"routes": [
|
|
489
|
+
"mainRoute": convertRoute(route: route),
|
|
490
|
+
"alternativeRoutes": []
|
|
491
|
+
]
|
|
492
|
+
])
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
func navigationViewControllerDidDismiss(
|
|
496
|
+
_ navigationViewController: NavigationViewController,
|
|
497
|
+
byCanceling canceled: Bool
|
|
498
|
+
) { }
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
extension UIView {
|
|
502
|
+
func findViews<T: UIView>(subclassOf: T.Type) -> [T] {
|
|
503
|
+
return recursiveSubviews.compactMap { $0 as? T }
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
var recursiveSubviews: [UIView] {
|
|
507
|
+
return subviews + subviews.flatMap { $0.recursiveSubviews }
|
|
508
|
+
}
|
|
509
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sargsyansevak/expo-mapbox-navigation",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Expo module for Mapbox's Navigation SDK",
|
|
5
|
+
"readmeFilename": "README.md",
|
|
6
|
+
"main": "build/index.js",
|
|
7
|
+
"types": "build/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "expo-module build",
|
|
10
|
+
"clean": "expo-module clean",
|
|
11
|
+
"lint": "expo-module lint",
|
|
12
|
+
"test": "expo-module test",
|
|
13
|
+
"prepare": "expo-module prepare",
|
|
14
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
15
|
+
"expo-module": "expo-module",
|
|
16
|
+
"open:ios": "xed example/ios",
|
|
17
|
+
"open:android": "open -a \"Android Studio\" example/android"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"react-native",
|
|
21
|
+
"expo",
|
|
22
|
+
"mapbox",
|
|
23
|
+
"navigation",
|
|
24
|
+
"expo-mapbox-navigation",
|
|
25
|
+
"ExpoMapboxNavigation"
|
|
26
|
+
],
|
|
27
|
+
"repository": "https://github.com/SargsyanSevak/expo-mapbox-navigation",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/SargsyanSevak/expo-mapbox-navigation/issues"
|
|
30
|
+
},
|
|
31
|
+
"author": "sargsyansevak <thesevaksargsyan@gmail.com> (https://github.com/SargsyanSevak",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"homepage": "https://github.com/SargsyanSevak/expo-mapbox-navigation#readme",
|
|
34
|
+
"dependencies": {},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/react": "^18.0.25",
|
|
37
|
+
"expo-module-scripts": "^3.5.2",
|
|
38
|
+
"expo-modules-core": "~2.5.0",
|
|
39
|
+
"@expo/config-plugins": "~8.0.10"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"expo": "*",
|
|
43
|
+
"react": "*",
|
|
44
|
+
"react-native": "*",
|
|
45
|
+
"@rnmapbox/maps": "*"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/plugin/config.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const {
|
|
2
|
+
withDangerousMod,
|
|
3
|
+
withAndroidColors,
|
|
4
|
+
withAndroidColorsNight,
|
|
5
|
+
AndroidConfig,
|
|
6
|
+
} = require("@expo/config-plugins");
|
|
7
|
+
const fs = require("fs/promises");
|
|
8
|
+
const path = require("path");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* This plugin adds a post-install step to the Podfile that addresses linking issues between pods and xcframeworks.
|
|
12
|
+
* In this case, the navigation library is added as an xcframework, and the maps library is added as a pod
|
|
13
|
+
*
|
|
14
|
+
* See https://github.com/CocoaPods/CocoaPods/issues/11079#issuecomment-984670700
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const applyPodfilePostInstallModifications = (src, mapboxMapsVersion) => {
|
|
18
|
+
return (
|
|
19
|
+
`ENV['ExpoNavigationMapboxMapsVersion'] = '${mapboxMapsVersion}'\n` +
|
|
20
|
+
src.replace(
|
|
21
|
+
"post_install do |installer|",
|
|
22
|
+
`post_install do |installer|
|
|
23
|
+
installer.pods_project.targets.each do |target|
|
|
24
|
+
if (target.name.include? 'MapboxMaps' or target.name.include? 'Turf')
|
|
25
|
+
target.build_configurations.each do |config|
|
|
26
|
+
config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end`
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const withIosPostInstallStep = (config, mapboxMapsVersion) =>
|
|
35
|
+
withDangerousMod(config, [
|
|
36
|
+
"ios",
|
|
37
|
+
async (exportedConfig) => {
|
|
38
|
+
const file = path.join(
|
|
39
|
+
exportedConfig.modRequest.platformProjectRoot,
|
|
40
|
+
"Podfile"
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const contents = await fs.readFile(file, "utf8");
|
|
44
|
+
await fs.writeFile(
|
|
45
|
+
file,
|
|
46
|
+
applyPodfilePostInstallModifications(contents, mapboxMapsVersion),
|
|
47
|
+
"utf-8"
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return exportedConfig;
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
const withIosTokenInfoPlist = (config, accessToken) => {
|
|
55
|
+
if (!config.ios) {
|
|
56
|
+
config.ios = {};
|
|
57
|
+
}
|
|
58
|
+
if (!config.ios.infoPlist) {
|
|
59
|
+
config.ios.infoPlist = {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
config.ios.infoPlist["MBXAccessToken"] = accessToken;
|
|
63
|
+
|
|
64
|
+
return config;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const withIosConfig = (config, { accessToken, mapboxMapsVersion }) => {
|
|
68
|
+
const configWithPostInstallStep = withIosPostInstallStep(
|
|
69
|
+
config,
|
|
70
|
+
mapboxMapsVersion
|
|
71
|
+
);
|
|
72
|
+
const configWithAccessToken = withIosTokenInfoPlist(
|
|
73
|
+
configWithPostInstallStep,
|
|
74
|
+
accessToken
|
|
75
|
+
);
|
|
76
|
+
return configWithAccessToken;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const withAndroidConfig = (config, { androidColorOverrides }) => {
|
|
80
|
+
const configWithAndroidColors = withAndroidColors(config, (config) => {
|
|
81
|
+
let currentModResults = config.modResults;
|
|
82
|
+
|
|
83
|
+
for (const [name, value] of Object.entries(androidColorOverrides ?? {})) {
|
|
84
|
+
AndroidConfig.Colors.assignColorValue(currentModResults, { name, value });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
config.modResults = currentModResults;
|
|
88
|
+
|
|
89
|
+
return config;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const configWithAndroidColorsNight = withAndroidColorsNight(
|
|
93
|
+
configWithAndroidColors,
|
|
94
|
+
(config) => {
|
|
95
|
+
let currentModResults = config.modResults;
|
|
96
|
+
|
|
97
|
+
for (const [name, value] of Object.entries(androidColorOverrides ?? {})) {
|
|
98
|
+
AndroidConfig.Colors.assignColorValue(currentModResults, {
|
|
99
|
+
name,
|
|
100
|
+
value,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
config.modResults = currentModResults;
|
|
105
|
+
|
|
106
|
+
return config;
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
return configWithAndroidColorsNight;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const withConfig = (
|
|
114
|
+
config,
|
|
115
|
+
{ accessToken, mapboxMapsVersion, androidColorOverrides }
|
|
116
|
+
) => {
|
|
117
|
+
const configWithIos = withIosConfig(config, {
|
|
118
|
+
accessToken,
|
|
119
|
+
mapboxMapsVersion,
|
|
120
|
+
});
|
|
121
|
+
const configWithAndroid = withAndroidConfig(configWithIos, {
|
|
122
|
+
androidColorOverrides,
|
|
123
|
+
});
|
|
124
|
+
return configWithAndroid;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
module.exports = withConfig;
|