diff --git a/MapboxCoreNavigation/RouteController.swift b/MapboxCoreNavigation/RouteController.swift index 07f915280bc..187bc31e742 100644 --- a/MapboxCoreNavigation/RouteController.swift +++ b/MapboxCoreNavigation/RouteController.swift @@ -229,8 +229,10 @@ open class RouteController: NSObject { if willChangeVisualIndex || isFirstLocation { let currentStepProgress = routeProgress.currentLegProgress.currentStepProgress currentStepProgress.visualInstructionIndex = Int(status.bannerInstruction?.index ?? 0) - let instruction = currentStepProgress.currentVisualInstruction - announcePassage(of: instruction!, routeProgress: routeProgress) + + if let instruction = currentStepProgress.currentVisualInstruction { + announcePassage(of: instruction, routeProgress: routeProgress) + } } } @@ -258,7 +260,11 @@ open class RouteController: NSObject { let advancesToNextLeg = delegate?.router?(self, didArriveAt: currentDestination) ?? DefaultBehavior.didArriveAtWaypoint guard !routeProgress.isFinalLeg && advancesToNextLeg else { return } - routeProgress.legIndex = Int(status.legIndex) + if advancesToNextLeg { + let legIndex = status.legIndex + 1 + navigator.changeRouteLeg(forRoute: 0, leg: legIndex) + routeProgress.legIndex = Int(legIndex) + } } } } diff --git a/MapboxCoreNavigationTests/NavigationServiceTests.swift b/MapboxCoreNavigationTests/NavigationServiceTests.swift index f945f0c1fc3..5d15570b771 100644 --- a/MapboxCoreNavigationTests/NavigationServiceTests.swift +++ b/MapboxCoreNavigationTests/NavigationServiceTests.swift @@ -300,7 +300,7 @@ class NavigationServiceTests: XCTestCase { let navigation = dependencies.navigationService let now = Date() - let trace = Fixture.generateTrace(for: route).enumerated().map { $0.element.shifted(to: now + $0.offset) } + let trace = Fixture.generateTrace(for: route).shiftedToPresent() trace.forEach { navigation.router!.locationManager!(navigation.locationManager, didUpdateLocations: [$0]) } // TODO: Verify why we need a second location update when routeState == .complete to trigger `MMEEventTypeNavigationArrive` @@ -328,7 +328,7 @@ class NavigationServiceTests: XCTestCase { // MARK: When navigation begins with a location update let now = Date() - let trace = Fixture.generateTrace(for: route).enumerated().map { $0.element.shifted(to: now + $0.offset) } + let trace = Fixture.generateTrace(for: route).shiftedToPresent() trace.forEach { navigation.router.locationManager!(navigation.locationManager, didUpdateLocations: [$0]) } @@ -405,4 +405,26 @@ class NavigationServiceTests: XCTestCase { subject.poorGPSPatience = 5.0 XCTAssert(subject.poorGPSTimer.countdownInterval == .milliseconds(5000), "Timer should now have a countdown interval of 5000 millseconds.") } + + func testMultiLegRoute() { + let route = Fixture.route(from: "multileg-route") + let trace = Fixture.generateTrace(for: route).shiftedToPresent().qualified() + let service = dependencies.navigationService + + let routeController = service.router as! RouteController + routeController.route = route + + for (index, location) in trace.enumerated() { + + service.locationManager!(service.locationManager, didUpdateLocations: [location]) + + if index < 33 { + XCTAssert(routeController.routeProgress.legIndex == 0) + } else { + XCTAssert(routeController.routeProgress.legIndex == 1) + } + } + + XCTAssertTrue(delegate.recentMessages.contains("navigationService(_:didArriveAt:)")) + } } diff --git a/MapboxNavigation.xcodeproj/project.pbxproj b/MapboxNavigation.xcodeproj/project.pbxproj index 79571f7e54e..d146c04d4c3 100644 --- a/MapboxNavigation.xcodeproj/project.pbxproj +++ b/MapboxNavigation.xcodeproj/project.pbxproj @@ -98,6 +98,7 @@ 35726EE81F0856E900AFA1B6 /* DayStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35726EE71F0856E900AFA1B6 /* DayStyle.swift */; }; 3573EA71215A5A9F009899D7 /* RouteControllerSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3573EA70215A5A9F009899D7 /* RouteControllerSnapshotTests.swift */; }; 3577B878214FF35800094294 /* FavoritesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3577B877214FF35800094294 /* FavoritesList.swift */; }; + 357DEC44221AEE150019BAEC /* multileg-route.json in Resources */ = {isa = PBXBuildFile; fileRef = 357DEC43221AEE150019BAEC /* multileg-route.json */; }; 3582A25020EEC46B0029C5DE /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3582A24F20EEC46B0029C5DE /* Router.swift */; }; 3582A25220EFA9680029C5DE /* RouterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3582A25120EFA9680029C5DE /* RouterDelegate.swift */; }; 358D14661E5E3B7700ADE590 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 358D14651E5E3B7700ADE590 /* AppDelegate.swift */; }; @@ -685,6 +686,7 @@ 35726EE71F0856E900AFA1B6 /* DayStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayStyle.swift; sourceTree = ""; }; 3573EA70215A5A9F009899D7 /* RouteControllerSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouteControllerSnapshotTests.swift; sourceTree = ""; }; 3577B877214FF35800094294 /* FavoritesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FavoritesList.swift; path = Example/FavoritesList.swift; sourceTree = ""; }; + 357DEC43221AEE150019BAEC /* multileg-route.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "multileg-route.json"; sourceTree = ""; }; 357F0DF01EB9D99F00A0B53C /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; 357F0DF11EB9DAB400A0B53C /* vi */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; 3582A24F20EEC46B0029C5DE /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; @@ -1142,6 +1144,7 @@ 35CDA8882190F5100072B675 /* sthlm-double-back-replay.json */, 35CDA8852190F50A0072B675 /* sthlm-double-back.json */, C5EF397420599120009A2C50 /* straight-line.json */, + 357DEC43221AEE150019BAEC /* multileg-route.json */, ); path = Fixtures; sourceTree = ""; @@ -2141,6 +2144,7 @@ 35CDA8972190F6980072B675 /* sthlm-double-back.json in Resources */, 35CDA88F2190F6980072B675 /* EmptyStyle.json in Resources */, 35CDA8992190F6980072B675 /* UnionSquare-to-GGPark.route in Resources */, + 357DEC44221AEE150019BAEC /* multileg-route.json in Resources */, 35C8DBF6219194380053328C /* routeWithTunnels_9thStreetDC.json in Resources */, 35C8DBF52191940C0053328C /* straight-line.json in Resources */, 35C8DC0F2191DE940053328C /* DCA-Arboretum.trace.json in Resources */, diff --git a/TestHelper/CoreLocation.swift b/TestHelper/CoreLocation.swift index 8e5d5d4b559..bfb20a9970a 100644 --- a/TestHelper/CoreLocation.swift +++ b/TestHelper/CoreLocation.swift @@ -131,3 +131,29 @@ extension Date { return formatter }() } + +extension Array where Element == CLLocation { + + // Shifts the [CLLocation]’s first location to now and offsets the remaining locations by one second after the prior. + public func shiftedToPresent() -> [CLLocation] { + let now = Date() + return enumerated().map { CLLocation(coordinate: $0.element.coordinate, + altitude: $0.element.altitude, + horizontalAccuracy: $0.element.horizontalAccuracy, + verticalAccuracy: $0.element.verticalAccuracy, + course: $0.element.course, + speed: $0.element.speed, + timestamp: now + $0.offset) } + } + + // Returns a [CLLocation] with course and accuracies qualified for navigation native. + public func qualified() -> [CLLocation] { + return enumerated().map { CLLocation(coordinate: $0.element.coordinate, + altitude: -1, + horizontalAccuracy: 10, + verticalAccuracy: -1, + course: -1, + speed: 10, + timestamp: $0.element.timestamp) } + } +} diff --git a/TestHelper/Fixtures/multileg-route.json b/TestHelper/Fixtures/multileg-route.json new file mode 100644 index 00000000000..9335940ae43 --- /dev/null +++ b/TestHelper/Fixtures/multileg-route.json @@ -0,0 +1 @@ +{"routes":[{"routeOptions":{"profile":"auto","uuid":"offline","user":"mapbox","baseUrl":"https://api.mapbox.com","coordinates":[[9.519175,47.210821],[9.522274,47.214263],[9.512569,47.212326]],"geometries":"polyline6","overview":"full","steps":true,"language":"en-US","voice_instructions":true,"voice_units":"imperial","banner_instructions":true,"annotations":"duration,congestion","alternatives":true,"continue_straight":true,"roundabout_exits":true,"access_token":"foo"},"weight_name":"Valhalla default","legs":[{"steps":[{"name":"Haldengasse","distance":377.4,"duration":40,"intersections":[{"entry":[true],"out":0,"bearings":[65],"location":[9.519171,47.210823]},{"out":0,"entry":[true,true,false],"in":2,"bearings":[60,148,245],"location":[9.519632,47.210968]},{"out":0,"entry":[true,true,false],"in":2,"bearings":[55,146,240],"location":[9.519948,47.21109]},{"out":0,"entry":[true,true,false],"in":2,"bearings":[41,130,231],"location":[9.520215,47.21122]},{"out":0,"entry":[true,false,true],"in":1,"bearings":[50,220,275],"location":[9.520606,47.211536]},{"out":0,"entry":[true,true,false],"in":2,"bearings":[39,120,230],"location":[9.52073,47.211605]},{"out":0,"entry":[true,true,false],"in":2,"bearings":[34,130,214],"location":[9.521328,47.212181]},{"out":0,"entry":[true,false,true],"in":1,"bearings":[35,214,298],"location":[9.52139,47.212242]},{"out":0,"entry":[true,false,true],"in":1,"bearings":[62,206,294],"location":[9.522117,47.213245]}],"weight":40,"driving_side":"right","mode":"driving","maneuver":{"type":"depart","bearing_after":65,"bearing_before":0,"location":[9.519171,47.210823],"instruction":"Head northeast on Haldengasse"},"geometry":"mso`yAcc_dQaH{[sFwRcAmD}DgJmEwFkLuOiCwFuIsKiXuWyB{BuJqJyQcMuTsKcFcCkB_Bu@sBkAwF","voiceInstructions":[{"distanceAlongGeometry":377.4,"announcement":"Head northeast on Haldengasse, then turn left onto Sankt Martins-Ring","ssmlAnnouncement":"Head northeast on Haldengasse, then turn left onto Sankt Martins-Ring"},{"distanceAlongGeometry":141.5,"announcement":"Turn left onto Sankt Martins-Ring, then you will arrive at your 1st destination","ssmlAnnouncement":"Turn left onto Sankt Martins-Ring, then you will arrive at your 1st destination"}],"bannerInstructions":[{"distanceAlongGeometry":377.4,"primary":{"type":"turn","modifier":"left","degrees":0,"driving_side":"","components":[{"text":"Sankt Martins-Ring","type":"text","abbr":""}],"text":"Sankt Martins-Ring"},"secondary":null}]},{"name":"Sankt Martins-Ring","distance":117,"duration":18,"intersections":[{"out":2,"entry":[true,false,true],"in":1,"bearings":[144,242,347],"location":[9.522299,47.21331]},{"out":0,"entry":[true,false,false],"in":1,"bearings":[2,177,277],"location":[9.522253,47.213512]},{"out":2,"entry":[true,false,true],"in":1,"bearings":[83,182,359],"location":[9.522255,47.213547]},{"out":0,"entry":[true,true,false],"in":2,"bearings":[14,92,184],"location":[9.522265,47.213902]},{"out":2,"entry":[true,false,true],"in":1,"bearings":[77,200,355],"location":[9.52236,47.214111]}],"weight":18,"driving_side":"right","mode":"driving","maneuver":{"type":"end of road","modifier":"left","bearing_after":347,"bearing_before":62,"location":[9.522299,47.21331],"_merged_bearing_after":359,"instruction":"Turn left onto Sankt Martins-Ring"},"geometry":"{nt`yAsfedQwCjAgDLsA?cAAuE@eFDoCKyBQgDy@{Aa@sAs@kAk@cAc@yBBkAlAe@xAGpC","voiceInstructions":[{"distanceAlongGeometry":65,"announcement":"You have arrived at your 1st destination","ssmlAnnouncement":"You have arrived at your 1st destination"}],"bannerInstructions":[{"distanceAlongGeometry":117,"primary":{"type":"arrive","modifier":"straight","degrees":0,"driving_side":"","components":[{"text":"You will arrive","type":"text","abbr":""}],"text":"You will arrive"},"secondary":null},{"distanceAlongGeometry":65,"primary":{"type":"arrive","modifier":"straight","degrees":0,"driving_side":"","components":[{"text":"You have arrived","type":"text","abbr":""}],"text":"You have arrived"},"secondary":null}]},{"name":"Alemannenstrasse","distance":0,"duration":0,"intersections":[{"entry":[true],"in":0,"bearings":[121],"location":[9.522219,47.214268]}],"weight":0,"driving_side":"right","mode":"driving","maneuver":{"type":"arrive","bearing_after":0,"bearing_before":301,"location":[9.522219,47.214268],"instruction":"You have arrived at your 1st destination"},"geometry":"ujv`yAsaedQ","voiceInstructions":[],"bannerInstructions":[]}],"weight":58,"distance":494.4,"annotation":{"distance":[38.4,28,25,46,12,79,8,125,16,23,4,40,25,25],"duration":[2,4,3,5,2,8,2,11,3,5,2,4,3,4]},"duration":58,"summary":"TODO"},{"steps":[{"name":"Alemannenstrasse","distance":245,"duration":25,"intersections":[{"entry":[true],"out":0,"bearings":[229],"location":[9.52222,47.214268]},{"out":2,"entry":[false,false,true],"in":0,"bearings":[31,158,221],"location":[9.521856,47.213913]},{"out":1,"entry":[false,true,false],"in":0,"bearings":[41,226,320],"location":[9.521726,47.213814]},{"out":2,"entry":[false,false,true],"in":0,"bearings":[50,149,230],"location":[9.520467,47.213051]},{"out":2,"entry":[false,false,true],"in":0,"bearings":[58,187,275],"location":[9.520022,47.212826]}],"weight":25,"driving_side":"right","mode":"driving","maneuver":{"type":"depart","bearing_after":229,"bearing_before":0,"location":[9.52222,47.214268],"instruction":"Head southwest on Alemannenstrasse"},"geometry":"ujv`yAuaedQ\\zAl@rArA~AxBhBbGdEfDtCdEdGvC~EnN~UtJrSvN`[tIpR|@~Bl@dCFfAGtAO`C","voiceInstructions":[{"distanceAlongGeometry":245,"announcement":"Head southwest on Alemannenstrasse, then make a slight left onto Kappelestrasse","ssmlAnnouncement":"Head southwest on Alemannenstrasse, then make a slight left onto Kappelestrasse"},{"distanceAlongGeometry":147,"announcement":"Make a slight left onto Kappelestrasse","ssmlAnnouncement":"Make a slight left onto Kappelestrasse"}],"bannerInstructions":[{"distanceAlongGeometry":245,"primary":{"type":"turn","modifier":"slight left","degrees":0,"driving_side":"","components":[{"text":"Kappelestrasse","type":"text","abbr":""}],"text":"Kappelestrasse"},"secondary":null}]},{"name":"Kappelestrasse","distance":165,"duration":18,"intersections":[{"out":1,"entry":[false,true,true],"in":0,"bearings":[95,250,359],"location":[9.519878,47.212833]},{"out":1,"entry":[false,true,false],"in":0,"bearings":[70,253,338],"location":[9.519689,47.212788]},{"out":2,"entry":[false,false,true],"in":0,"bearings":[73,146,252],"location":[9.519138,47.212673]},{"out":2,"entry":[true,false,true],"in":1,"bearings":[53,72,252],"location":[9.518672,47.212578]}],"weight":18,"driving_side":"right","mode":"driving","maneuver":{"type":"turn","modifier":"slight left","bearing_after":250,"bearing_before":275,"location":[9.519878,47.212833],"instruction":"Make a slight left onto Kappelestrasse"},"geometry":"aqs`yAko`dQzAbJ?VbFja@jAzI|@zHt@lGl@jEz@`HhBtOpCxU","voiceInstructions":[{"distanceAlongGeometry":165,"announcement":"In 600 feet, make a slight right onto Kappelestrasse","ssmlAnnouncement":"In 600 feet, make a slight right onto Kappelestrasse"},{"distanceAlongGeometry":137.5,"announcement":"Make a slight right onto Kappelestrasse, then make a slight right onto Rofenbergstrasse","ssmlAnnouncement":"Make a slight right onto Kappelestrasse, then make a slight right onto Rofenbergstrasse"}],"bannerInstructions":[{"distanceAlongGeometry":165,"primary":{"type":"turn","modifier":"slight right","degrees":0,"driving_side":"","components":[{"text":"Kappelestrasse","type":"text","abbr":""}],"text":"Kappelestrasse"},"secondary":null},{"distanceAlongGeometry":137.5,"primary":{"type":"turn","modifier":"slight right","degrees":0,"driving_side":"","components":[{"text":"Kappelestrasse","type":"text","abbr":""}],"text":"Kappelestrasse"},"secondary":null,"sub":{"type":"turn","modifier":"slight right","degrees":0,"driving_side":"","components":[{"text":"Rofenbergstrasse","type":"text","abbr":""}],"text":"Rofenbergstrasse"}}]},{"name":"Kappelestrasse","distance":31,"duration":6,"intersections":[{"out":2,"entry":[false,true,true],"in":0,"bearings":[75,246,289],"location":[9.517793,47.212399]}],"weight":6,"driving_side":"right","mode":"driving","maneuver":{"type":"turn","modifier":"slight right","bearing_after":289,"bearing_before":255,"location":[9.517793,47.212399],"instruction":"Make a slight right onto Kappelestrasse"},"geometry":"{ur`yA_m|cQU`CO~AcBvGs@tF","voiceInstructions":[{"distanceAlongGeometry":31,"announcement":"Make a slight right onto Rofenbergstrasse, then turn left onto Eichenstrasse","ssmlAnnouncement":"Make a slight right onto Rofenbergstrasse, then turn left onto Eichenstrasse"}],"bannerInstructions":[{"distanceAlongGeometry":31,"primary":{"type":"turn","modifier":"slight right","degrees":0,"driving_side":"","components":[{"text":"Rofenbergstrasse","type":"text","abbr":""}],"text":"Rofenbergstrasse"},"secondary":null,"sub":{"type":"turn","modifier":"left","degrees":0,"driving_side":"","components":[{"text":"Eichenstrasse","type":"text","abbr":""}],"text":"Eichenstrasse"}}]},{"name":"Rofenbergstrasse","distance":40,"duration":5,"intersections":[{"out":2,"entry":[false,true,true],"in":0,"bearings":[112,183,322],"location":[9.517416,47.212494]}],"weight":5,"driving_side":"right","mode":"driving","maneuver":{"type":"turn","modifier":"slight right","bearing_after":322,"bearing_before":292,"location":[9.517416,47.212494],"instruction":"Make a slight right onto Rofenbergstrasse"},"geometry":"y{r`yAou{cQ}EvF{KvF","voiceInstructions":[{"distanceAlongGeometry":40,"announcement":"Turn left onto Eichenstrasse","ssmlAnnouncement":"Turn left onto Eichenstrasse"}],"bannerInstructions":[{"distanceAlongGeometry":40,"primary":{"type":"turn","modifier":"left","degrees":0,"driving_side":"","components":[{"text":"Eichenstrasse","type":"text","abbr":""}],"text":"Eichenstrasse"},"secondary":null}]},{"name":"Eichenstrasse","distance":359.8,"duration":41,"intersections":[{"out":2,"entry":[true,false,true,true],"in":1,"bearings":[66,158,254,329],"location":[9.517169,47.212811]},{"out":2,"entry":[false,true,true],"in":0,"bearings":[74,156,264],"location":[9.517041,47.212784]},{"out":1,"entry":[false,true,true],"in":0,"bearings":[87,258,339],"location":[9.51637,47.212757]},{"out":1,"entry":[false,true,true],"in":0,"bearings":[63,237,328],"location":[9.515616,47.212593]},{"out":1,"entry":[false,true,true],"in":0,"bearings":[57,241,329],"location":[9.515471,47.212532]},{"out":2,"entry":[false,true,true,false],"in":0,"bearings":[51,163,252,298],"location":[9.515166,47.212391]}],"weight":41,"driving_side":"right","mode":"driving","maneuver":{"type":"turn","modifier":"left","bearing_after":254,"bearing_before":338,"location":[9.517169,47.212811],"instruction":"Turn left onto Eichenstrasse"},"geometry":"sos`yA_f{cQt@|FLzFd@ba@t@jMd@pGzArIzAlHr@`CrAdEd@zAhC`JpC~FbAzHz@zFl@vJF~DFvD}@`xB","voiceInstructions":[{"distanceAlongGeometry":359.8,"announcement":"In a quarter mile, you will arrive at your destination","ssmlAnnouncement":"In a quarter mile, you will arrive at your destination"},{"distanceAlongGeometry":87.8,"announcement":"You have arrived at your destination","ssmlAnnouncement":"You have arrived at your destination"}],"bannerInstructions":[{"distanceAlongGeometry":359.8,"primary":{"type":"arrive","modifier":"straight","degrees":0,"driving_side":"","components":[{"text":"You will arrive","type":"text","abbr":""}],"text":"You will arrive"},"secondary":null},{"distanceAlongGeometry":87.8,"primary":{"type":"arrive","modifier":"straight","degrees":0,"driving_side":"","components":[{"text":"You have arrived","type":"text","abbr":""}],"text":"You have arrived"},"secondary":null}]},{"name":"Eichenstrasse","distance":0,"duration":0,"intersections":[{"entry":[true],"in":0,"bearings":[92],"location":[9.512569,47.212326]}],"weight":0,"driving_side":"right","mode":"driving","maneuver":{"type":"arrive","bearing_after":0,"bearing_before":272,"location":[9.512569,47.212326],"instruction":"You have arrived at your destination"},"geometry":"kqr`yAqfrcQ","voiceInstructions":[],"bannerInstructions":[]}],"weight":95,"distance":840.8,"annotation":{"distance":[49,15,128,42,11,15,44,37,69,31,40,10,51,60,13,28,197.8],"duration":[3,3,10,6,3,3,5,4,6,6,5,3,6,5,3,3,21]},"duration":95,"summary":"TODO"}],"duration":153,"distance":1335.2,"weight":153,"geometry":"mso`yAcc_dQaH{[sFwRcAmD}DgJmEwFkLuOiCwFuIsKiXuWyB{BuJqJyQcMuTsKcFcCkB_Bu@sBkAwFwCjAgDLsA?cAAuE@eFDoCKyBQgDy@{Aa@sAs@kAk@cAc@yBBkAlAe@xAGpC\\xAl@rArA~AxBhBbGdEfDtCdEdGvC~EnN~UtJrSvN`[tIpR|@~Bl@dCFfAGtAO`CzAbJ?VbFja@jAzI|@zHt@lGl@jEz@`HhBtOpCxUU`CO~AcBvGs@tF}EvF{KvFt@|FLzFd@ba@t@jMd@pGzArIzAlHr@`CrAdEd@zAhC`JpC~FbAzHz@zFl@vJF~DFvD}@`xB","voiceLocale":"en-US"}],"waypoints":[{"name":"Haldengasse","hint":"TODO","distance":0,"location":[9.519172,47.210823]},{"name":"Alemannenstrasse","hint":"TODO","distance":0,"location":[9.52222,47.214268]},{"name":"Eichenstrasse","hint":"TODO","distance":0,"location":[9.512569,47.212326]}],"code":"Ok"}