Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
jumbopilot opened this issue Sep 27, 2018 · 5 comments

Comments

@jumbopilot
Copy link

jumbopilot commented Sep 27, 2018

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
Copy link
Contributor

1ec5 commented Dec 7, 2019

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:

func updateCameraAltitude(for routeProgress: RouteProgress, completion: CompletionHandler? = nil) {
guard mapView.tracksUserCourse else { return } //only adjust when we are actively tracking user course
let zoomOutAltitude = mapView.zoomedOutMotorwayAltitude
let defaultAltitude = mapView.defaultAltitude
let isLongRoad = routeProgress.distanceRemaining >= mapView.longManeuverDistance
let currentStep = routeProgress.currentLegProgress.currentStep
let upComingStep = routeProgress.currentLegProgress.upcomingStep
//If the user is at the last turn maneuver, the map should zoom in to the default altitude.
let currentInstruction = routeProgress.currentLegProgress.currentStepProgress.currentSpokenInstruction
//If the user is on a motorway, not exiting, and their segment is sufficently long, the map should zoom out to the motorway altitude.
//otherwise, zoom in if it's the last instruction on the step.
let currentStepIsMotorway = currentStep.isMotorway
let nextStepIsMotorway = upComingStep?.isMotorway ?? false
if currentStepIsMotorway, nextStepIsMotorway, isLongRoad {
setCamera(altitude: zoomOutAltitude)
} else if currentInstruction == currentStep.lastInstruction {
setCamera(altitude: defaultAltitude)
}
}

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
Copy link
Author

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
Copy link
Contributor

1ec5 commented Dec 10, 2019

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
Copy link

stale bot commented Jul 18, 2020

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 stale bot added the archived Archived issue. label Jul 18, 2020
@stale
Copy link

stale bot commented Aug 24, 2020

Stale.

@stale stale bot closed this as completed Aug 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants