mapbox / mapbox-navigation-ios

Turn-by-turn navigation logic and UI in Swift on iOS
https://docs.mapbox.com/ios/navigation/
Other
863 stars 313 forks source link

NavigationRouteOptions: Support for linear flight route by providing the new route option .flight #1747

Closed jumbopilot closed 4 years ago

jumbopilot commented 6 years ago

This is about providing a route based on 3D waypoints used as single destination point/coordinate by informing about the direction (just like a compass needle), linear travel distance and ETA mechanisms, but using the CLLocation data type instead of the OSM route (road bound) algorithm.

Something like simply overriding the NavigationRouteOptions

// Specify that the route is intended for automobiles avoiding traffic let options = NavigationRouteOptions(waypoints: [origin, destination], profileIdentifier: .automobileAvoidingTraffic)

or overriding the calculate method:

// Generate the route object and draw it on the map _ = Directions.shared.calculate(options) { [unowned self] (waypoints, routes, error) in self.directionsRoute = routes?.first }

Basically, I just have an origin (3D user location) and a destination (3D coordinate) and calculate the above mentioned metrics and provide them to the Directions object. Is this feasible?

I'd be happy Mapbox is evaluating this option in order to re-use the lower route guidance views including Distance, Direction, ETA from Mapbox.

An alternative could be to allow creating/reusing the lower navigation pane apart the remaining navigation kit.

1ec5 commented 4 years ago

If you want the navigation SDK to follow a predefined route without any consideration for the road network, you can create a Route whose shape is a great circle between the destinations. In navigation SDK v0.38, you’d pass a JSON-formatted dictionary into Route(json:waypoints:options:) to create this route. Once #2275 lands, you’ll decode the route from JSON data instead:

// https://docs.mapbox.com/api/navigation/#route-object
let routeJSON: [String: Any?] = [
    "duration": 3 * 3600,
    "distance": 3_000_000,
    "geometry": greatCircle,
    "legs": [
        // https://docs.mapbox.com/api/navigation/#route-leg-object
        [
            "duration": 3 * 3600,
            "distance": 3_000_000,
            "steps": [
                [
                    "driving_side": "right",
                    "geometry": greatCircle,
                    "mode": "driving",
                    "maneuver": [
                        "bearing_before": 0,
                        "bearing_after": 0,
                        "location": [
                            greatCircle.coordinates[0].longitude,
                            greatCircle.coordinates[0].latitude,
                        ],
                        "type": "depart",
                        "instruction": "Take off",
                    ],
                    "weight": 1,
                    "duration": 3 * 3600,
                    "distance": 3_000_000,
                    "name": "",
                    "mode": "driving",
                    "bannerInstructions": [
                        [
                            "distanceAlongGeometry": 3_000_000,
                            "primary": [
                                "text": "Land",
                                "components": [
                                    [
                                        "text": "Land",
                                        "type": "text",
                                    ],
                                ],
                                "type": "arrive",
                            ],
                            "secondary": nil,
                        ],
                    ],
                ],
                [
                    "driving_side": "right",
                    "geometry": [
                        [greatCircle.coordinates[0].longitude, greatCircle.coordinates[0].latitude],
                        [greatCircle.coordinates[0].longitude, greatCircle.coordinates[0].latitude],
                    ],
                    "mode": "driving",
                    "maneuver": [
                        "bearing_before": 0,
                        "bearing_after": 0,
                        "location": [
                            greatCircle.coordinates[1].longitude,
                            greatCircle.coordinates[1].latitude,
                        ],
                        "type": "arrive",
                        "instruction": "Land",
                    ],
                    "weight": 1,
                    "duration": 3 * 3600,
                    "distance": 3_000_000,
                    "name": "",
                    "mode": "driving",
                    "bannerInstructions": [],
                ],
            ],
            "summary": "",
        ],
    ],
]
let routeData = try! JSONSerialization.data(withJSONObject: routeJSON, options: [])

let options = NavigationRouteOptions(waypoints: [origin, destination], profileIdentifier: DirectionsProfileIdentifier(rawValue: "jumbopilot/flying"))
options.shapeFormat = .geoJSON

let decoder = JSONDecoder()
decoder.userInfo[.options] = options
let route = try decoder.decode(Route.self, from: routeData)

Neither this SDK nor Turf provides a function for generating a great circle: mapbox/turf-swift#12. But you could port the Turf.js implementation, or if you’re OK with the inaccuracy of a straight-line animation in Spherical Mercator projection, you could use a straight linestring.

The other consideration is that the SDK’s default zoom level is at ground level. You’d want the zoom level to vary parabolically. That would require changes to this method:

https://github.com/mapbox/mapbox-navigation-ios/blob/9cbbd2f9c8019041f3e8e47f4c3a503498152da5/MapboxNavigation/RouteMapViewController.swift#L288-L309

If your goal is to simply imitate a flying motion, then the map SDK’s MGLMapView.fly(to:duration:peakAltitude:completionHandler:) method would be much more straightforward than repurposing turn-by-turn navigation for flight.

jumbopilot commented 4 years ago

Hi Minh,

I'm glad this topic gets more attention.

My requirements/intention are:

  1. create a 2D route containing multiple 2D waypoints (even though I'm considering 3D); the waypoint route may be imported or created on the map.
  2. Draw this route on the map (inaccuracy of a straight-line is OK as a first step, even over longer distance by taking care of waypoint distances not becoming too large)
  3. Use original Route Guidance/Direcitons (UI) of Mapbox Navigation SDK (disconnected from road network; straight line) to navigate from waypoint to waypoint, calculate/display ETA (Estimated Time of Arrival) based on current speed to next waypoint and final destination
  4. once destination is reached, a 3D replay this time considering the elevation/altitude (camera fly) as recorded (3D track), ideally on a 3D relief map

The last one is a long term milestone. Others are short term milestones.

Related to 1.: I understand Navigation SDK will soon be extended to consider JSON route like described above, which is a feasible way to me. Why wouldn't I be able to use the core navigation waypoint route guidance as is? Because standard route guidance is connected to the road network only? Wouldn't it make sense to allow such a functional switch to disregard the road network nd have this .flight route option?

Will point 2. and 3. be supported by Navigation SDK?

1ec5 commented 4 years ago

To be honest, flying (or swimming or sailing) use cases are nowhere near the top of our priority list. We have our hands full with only four routing profiles as it is, but we also prize customizability to handle unexpected use cases. Consider the huge code block above nothing more than a workaround. 🙂

A basic assumption of the turn-by-turn navigation functionality in the navigation SDK is that it compares your actual location to a predefined geometry. (Substitute your actual location for a fake one when route simulation is enabled.) But it’s a fair request to have more granular control over camera motion, which a flight simulator–like application might use to provide a much simpler geometry than the Directions API would – and no turn maneuvers.

Another issue you’ll run into is that the map SDK doesn’t currently support tilting (increasing the pitch) beyond 60 degrees or so: mapbox/mapbox-gl-native#6908. At that pitch, the camera still looks slightly downward instead of straight ahead as you’d expect of a conventional aircraft. There is a workaround for this limitation – lowering the horizon – but it causes severe performance degradation: mapbox/mapbox-gl-native#15163.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] commented 4 years ago

Stale.