When Driving Navigation Arrow Going Beyond Map Actual Route

1328
8
09-25-2018 07:04 AM
ManasaParida
New Contributor III

Dear All,

I was trying to test the map-app-ios with few customization from my side using AGSGeometryEngine Class to speech the direction text, after my integration i was trying to text the app how it was behaving when i am driving. Here with i am attaching 2 screenshot which i found at the time of testing the app, necessary help would be appreciate for the way forward to achieve my goal.

Also here i am attaching few code snippet how i was trying to achieve the text to speech recogniser.

// Tracking the current/updated new location from locatidHandler

mapView.locationDisplay.locatidHandler = { [weak self] location in

            // Have a new function and passing the new location to it.

            self?.callFunctionAndPlayWith(newLocation: location)

        }

// Function Implementation

func  callFunctionAndPlayWith(newLocation: AGSLocation){

        // check the Location Point is there or not

        guard newLocation.position != nil else {

            print("Unable to found the new location position")

            return

        }

        // get the geodeticBufferGeometry from newLocation.position

        let geodeticBufferGeometryOfCurrentLocation = self.geodeticBufferGeometry(maneuverGeometry: newLocation.position!)

        // Get the route result from Mode

        switch self.mode {

        case .routeResult(let route):

            // Enumerate the array to get the actual index data which intersect the geodeticGeometryValue

            route.directionManeuvers.enumerated().forEach { (index, value) in

                // Skipping the first Index as its my start location (for testing)

                if(index != 0){

                    // Intersected Index Found from directionManeuvers Array

                    let isUserIntersectsToPlannedRoute =                      AGSGeometryEngine.geometry(geodeticBufferGeometryOfCurrentLocation, intersects: value.geometry!)

                     // Check if if intersected then good to go

                    if(isUserIntersectsToPlannedRoute == true) {

                        // Check if we already speech then no need to speech again the same direction text

                        // Else speech as its a newly intersected point to speech

                        if(self.currentDirectionManeuver?.directionText != value.directionText){

                            let newInstance = DirectionsDisplayViewController()

                            newInstance.textToSpeech(directionText: value.directionText)

                            self.currentDirectionManeuver = value

                        }

                    }

                }

            }

        default:

            print("Mode is default")

        }

    }

func geodeticBufferGeometry(maneuverGeometry geometry:AGSGeometry) -> AGSPolygon {

        // Capturing 40 Meters Distance for the geodeticBufferGeometry to check the intersect to speech the new route.

        return AGSGeometryEngine.geodeticBufferGeometry(geometry, distance: 40, distanceUnit: AGSLinearUnit.meters(), maxDeviation: 1, curveType: AGSGeodeticCurveType.shapePreserving)!

    }

The above changes i have done to test the app.

But When Driving Navigation Arrow Going Beyond Map Actual Route or Road. please have a look on the below attached screenshot.

Would be great and helpful if you can suggest some trick how to test the app with Real Time Driving and Navigation.

0 Kudos
8 Replies
ManasaParida
New Contributor III

Any update from ArcGIS Runtime SDK for iOS‌ support team please ?

0 Kudos
Nicholas-Furness
Esri Regular Contributor

Hey Manasa Parida‌,

I wonder if there's some confusion about the purpose of the Maps App here?

The Runtime SDK does not offer a navigation experience (just a routing experience, i.e. we can get you directions, but you have to get yourself to your destination) and does not track you along a route that you've got planned. The Maps App doesn't add anything to that, so the fact that your location has deviated from the route, as in the screenshots above, is expected. You would need to add additional logic to your app if you want the location snapped to the route task result.

One approach might be to write your own AGSLocationDataSource that you pass a route task result to and as your data source reads updated locations from Core Location, your custom data source snaps the results to the current route, returning those to the AGSLocationDisplay. That way the map view will show you on the route if you're nearby.

I hope I've understood your question properly. I think the crux of it is that there's no relationship between the route task result that you're displaying and the device's displayed location, and that is as designed.

Nick.

0 Kudos
ManasaParida
New Contributor III

Hi Nicholas,

Just to reminding the old post, if we follow the above mentioned approach then i am bit wondering how our Custom data source (the one which we get from route task result as route information) will know the speed of the device movement because the custom data which we are injecting to AGSLocationDataSource is our route result also we are not passing any information in the custom data as speed to follow/navigate. Let's discuss into more depth, in my case if we have traffic and barriers then how we can manage those navigation movement based on the current road, how will AGSLocationDataSource will fire to invoke the locationChangedHandler to get frequent update. May be my approach is wrong to achieve or is there any other way you could suggest us to follow.

One approach i tried, it is also you only suggested : I crated a GPX file using some tool from google and injected that gpx file as AGSLocationDataSource for my testing purpose in that raw file i couldn't found the speed to follow the navigation it is going like crazy, i guess its going 100+ speed.

Snap to road which i am trying is using GeometryEngine Class

// I am coverting my routeTask Result directionManuever entire geometry to polyline

let polyline = directionManuever.geometry as? AGSPolyline

// Then using those polyline to create points of each 5 meters length using densifyGeometry
guard
let geodeticPath = AGSGeometryEngine.densifyGeometry(self.polyLine!, maxSegmentLength: 15) elsereturn }


// Assign the new 5 meters point to my global polyline variable , which i found from the above densifyGeometry

self.polyLine = geodeticPath as? AGSPolyline

// Now this polyline i am comparing with my currentlocation as buffer to snap the navigation symbol.

But it doesn't help me to achieve my goal.

Please have a look into my screen recorded scenario and help me out to do some out of box work.

0 Kudos
Nicholas-Furness
Esri Regular Contributor

Hi Manasa.

By default, the polyline will be turned into AGSLocations, with one for each point in the source polyline. Remember that if you densify a polyline, you will get the original points plus any densification results, so they might be less than maxSegmentLength apart.‌

Once you have called AGSSimulatedLocationDataSource.setLocationsWithPolyline, the AGSSimulatedLocationDataSource.locations array will be populated with AGSLocations. These locations will be provided to the AGSLocationDisplay at 1 location per second.

Each location in the locations array will initially have a velocity of -1 and a course of -1. You can modify the velocities and courses to a value >= 0 if need be. The AGSSimulatedLocationDataSource will emit a location every second. If it finds a velocity < 0, it will use 1 meter per second. If it finds a course < 0, it will calculate heading based off the direction from the previous location to the current location.

For the GPX data, it looks like we ignore the timestamps on the GPX source points, and emit one location every second regardless of the timing of the original data points. That probably explains why it looks like it's running too fast.

0 Kudos
tejveer
New Contributor

Hi @Manasa Parida 

we need to integrate in our application route always face up and map moving as user along the route.

I have looked your screen recording video and I want the same thing to happen in my app also i.e the map is moving when the user is moving. I am sharing a code snippet down please have a look. I am not able to achieve the same. Any help will be highly appreciated. Thanks in advance.

override func viewDidLoad() {

        super.viewDidLoad() 

         locationDisplay = self.mapView.locationDisplay

        locationDisplay.autoPanMode = AGSLocationDisplayAutoPanMode.navigation

        locationDisplay.navigationPointHeightFactor = 0.5

        locationDisplay.showAccuracy = true

}

0 Kudos
ManasaParida
New Contributor III

@tej veer , Hi sorry for the delayed reply,

could you please write me back what exactly you are looking for so that I can try my best to suggest you to achieve!! what you are looking out.

0 Kudos
ManasaParida
New Contributor III

Hi Nicholas Furness

Here is my code snippets to snap the Routing Result Manuever. But i am not able to assign snapped Point to the Runtime AGSLocation due to readOnly Property. 

@property (nullable, nonatomic, strong, readonly) AGSLocation *location

For the Testing purpose What i am doing here is ! For each locationChangedHandler update i am passing device GPS location and route solve result to a separate function like below code then doing some kind of iteration to find the snapped point based on the Current GPS Location called(AGSLocation), Then adding the new snapped point to a Graphics Layer. But what i am expecting here is how can i assign this snapped point to the Runtime Location so that it will have a smooth transition instead of Jumping. Please see the attached Video how it's showing currently.

Please suggest if there anything going with wrong approach.

    func snapedDirectionManeuverPointToRoad( routeManuevers: [AGSDirectionManeuver], deviceGPSPoint: AGSPoint) {

        var sortedDictionary:[Int:Double] = [:]

        let sortedKey: Int

        self.prepareDictionary = [:]

        

        // Loop through the entire route manuevers

        for routeManuever in 0...routeManuevers.count-1 {

            let maneuver = routeManuevers[routeManuever]

            let nearestCoord = AGSGeometryEngine.nearestCoordinate(in: maneuver.geometry!, to: deviceGPSPoint)

            self.prepareDictionary[routeManuever] = nearestCoord?.distance

        }

        

        // Keep the original value before Sorted

        sortedDictionary = self.prepareDictionary

        let sortedValue = self.prepareDictionary.values.sorted()

        let key = sortedDictionary.filter{$0.value == sortedValue[0]}

        sortedKey = key.keys.first!

        

        guard let densifyGeometry = AGSGeometryEngine.densifyGeometry(routeManuevers[sortedKey].geometry!, maxSegmentLength: 0.001) else {

            print("Unable to found densify result")

            return

        }

        

        // Find the nearest Vertex from the densified route point

        let nearestVertex = AGSGeometryEngine.nearestVertex(in: densifyGeometry, to: deviceGPSPoint)

        self.snapToRoad(snapedPoint: nearestVertex!.point, imageSymbol: AGSPictureMarkerSymbol(image: imageLiteral(resourceName: "Course")))

    }

    

    // Snapped point to show in Routing Overlay

    func snapToRoad(snapedPoint:AGSPoint, imageSymbol:AGSPictureMarkerSymbol) {

        let graphic = AGSGraphic(geometry: snapedPoint, symbol: imageSymbol, attributes: nil)

        self.routeNavigationSymbolOverlay.graphics.removeAllObjects()

        self.routeNavigationSymbolOverlay.graphics.add(graphic)

}

   

0 Kudos
Nicholas-Furness
Esri Regular Contributor

Hi Manasa. You're really close. You should just need to encapsulate your snap-to-road logic within a custom AGSLocationDataSource. When the mapview is provided new locations from that data source, it'll take care of smoothing interpolating between them.

I am pulling together a sample project and blog post to demonstrate this which I'll be sharing shortly.

0 Kudos