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

Make destination annotation methods public #4253

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
### Map

* Fixed an issue when `NavigationMapView` would not display a map, when using CarPlay and custom tile storage location. ([#4399](https://github.com/mapbox/mapbox-navigation-ios/pull/4399))
* Added `NavigationMapView.addDestinationAnnotation(_:identifier:styleLoaded:)`, `NavigationMapView.removeDestinationAnnotation(_:)` to present and remove the final destination annotation on a NavigationMapView. ([#4253](https://github.com/mapbox/mapbox-navigation-ios/pull/4253))

### Camera

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@
"repositoryURL": "https://github.com/mapbox/mapbox-common-ios.git",
"state": {
"branch": null,
"revision": "185d791e7478cc5da7cc2b4756ecd0d0bd387eac",
"version": "23.4.0-beta.1"
"revision": "d38abed1cfca3843308d478450c3d8b07a797c5a",
"version": "23.4.0-rc.1"
}
},
{
"package": "MapboxCoreMaps",
"repositoryURL": "https://github.com/mapbox/mapbox-core-maps-ios.git",
"state": {
"branch": null,
"revision": "81b929488b5bbc467df4cf294ceffa0ae98ef0e0",
"version": "10.12.0-beta.1"
"revision": "82706a65bf7a51818d943c91f014d17588b0f65e",
"version": "10.12.0-rc.1"
}
},
{
Expand All @@ -60,8 +60,8 @@
"repositoryURL": "https://github.com/mapbox/mapbox-maps-ios.git",
"state": {
"branch": null,
"revision": "246fa104e4eec0fb913c036cb0505723e26a9a2e",
"version": "10.12.0-beta.1"
"revision": "e47d5d6f2b55b0decd9d60b47b3c9c9b6c726fa3",
"version": "10.12.0-rc.1"
}
},
{
Expand Down Expand Up @@ -123,8 +123,8 @@
"repositoryURL": "https://github.com/apple/swift-argument-parser",
"state": {
"branch": null,
"revision": "fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a",
"version": "1.2.2"
"revision": "fddd1c00396eed152c45a46bea9f47b98e59301d",
"version": "1.2.0"
}
},
{
Expand Down
46 changes: 30 additions & 16 deletions Sources/MapboxNavigation/NavigationMapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1273,7 +1273,7 @@ open class NavigationMapView: UIView {
final destination `PointAnnotation` will be stored in this property and added to the `MapView`
later on.
*/
var finalDestinationAnnotations: [PointAnnotation] = []
private(set) var finalDestinationAnnotations: [PointAnnotation] = []

/**
Adds the route waypoints to the map given the current leg index. Previous waypoints for completed legs will be omitted.
Expand Down Expand Up @@ -1335,15 +1335,25 @@ open class NavigationMapView: UIView {

if let lastLeg = route.legs.last,
let destinationCoordinate = lastLeg.destination?.coordinate {
addDestinationAnnotation(destinationCoordinate)
addDestinationAnnotation(destinationCoordinate, identifier: AnnotationIdentifier.finalDestinationAnnotation)
}
}

func addDestinationAnnotation(_ coordinate: CLLocationCoordinate2D,
identifier: String = NavigationMapView.AnnotationIdentifier.finalDestinationAnnotation) {

/**
Adds a final destination annotation to the map. The annotation will be added only after fully loading `MapView` style. In such case
final destination will be stored and added to the `MapView` later on. `delegate` will be notified about the change via
`NavigationMapViewDelegate.navigationMapView(_:didAdd:pointAnnotationManager:)` method.

- parameter coordinate: Coordinate which represents the annotation location.
- parameter identifier: String to uniquely identify the destination annotation. Defaults to `nil` and a default identifier will be provided.
*/
public func addDestinationAnnotation(_ coordinate: CLLocationCoordinate2D,
identifier: String? = nil) {
let count = pointAnnotationManager?.annotations.count ?? finalDestinationAnnotations.count
let identifier = identifier ?? "\(AnnotationIdentifier.finalDestinationAnnotation)_\(count)"
var destinationAnnotation = PointAnnotation(id: identifier, coordinate: coordinate)
destinationAnnotation.image = .init(image: .defaultMarkerImage, name: ImageIdentifier.markerImage)

// If `PointAnnotationManager` is available - add `PointAnnotation`, if not - remember it
// and add it only after fully loading `MapView` style.
if let pointAnnotationManager = pointAnnotationManager {
Expand All @@ -1356,12 +1366,21 @@ open class NavigationMapView: UIView {
}
}

func removeDestinationAnnotation(_ identifier: String = NavigationMapView.AnnotationIdentifier.finalDestinationAnnotation) {
let remainingAnnotations = pointAnnotationManager?.annotations.filter {
$0.id != identifier
/**
Removes a final destination annotation from the map.

- parameter identifier: String to uniquely identify the destination annotation to be removed. Defaults to `nil` and removes all destination annotations with non-custom identifiers.
*/
public func removeDestinationAnnotation(identifier: String? = nil) {
let filter: (PointAnnotation) -> Bool
if let identifier = identifier {
filter = { $0.id == identifier }
} else {
filter = { $0.id.contains(AnnotationIdentifier.finalDestinationAnnotation) }
}

pointAnnotationManager?.annotations = remainingAnnotations ?? []

finalDestinationAnnotations.removeAll(where: filter)
pointAnnotationManager?.annotations.removeAll(where: filter)
}

/**
Expand Down Expand Up @@ -1433,11 +1452,6 @@ open class NavigationMapView: UIView {
*/
public var pointAnnotationManager: PointAnnotationManager?

func annotationsToRemove() -> [Annotation] {
let identifier = NavigationMapView.AnnotationIdentifier.finalDestinationAnnotation
return pointAnnotationManager?.annotations.filter({ $0.id == identifier }) ?? []
}

// MARK: Map Rendering and Observing

var routes: [Route]?
Expand Down
102 changes: 101 additions & 1 deletion Tests/MapboxNavigationTests/NavigationMapViewTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import XCTest
import MapboxDirections
import TestHelper
import Turf
import MapboxMaps
@testable import MapboxMaps
@testable import MapboxNavigation
@testable import MapboxCoreNavigation

Expand All @@ -13,6 +13,7 @@ class NavigationMapViewTests: TestCase {
]))
var navigationMapView: NavigationMapView!
var navigator: CoreNavigator!
var pointAnnotationManager: PointAnnotationManager!

let options: NavigationRouteOptions = .init(coordinates: [
CLLocationCoordinate2D(latitude: 40.311012, longitude: -112.47926),
Expand All @@ -22,11 +23,34 @@ class NavigationMapViewTests: TestCase {
let route = response.routes!.first!
return route
}()

private let coordinate1 = CLLocationCoordinate2D(latitude: 40.311012, longitude: -112.47926)
private let coordinate2 = CLLocationCoordinate2D(latitude: 30.176322, longitude: -102.806108)
private let finalDestinationAnnotationTestPrefix = "MapboxNavigation-MapboxNavigation-resources_finalDestinationAnnotation"

private final class DisplayLinkCoordinatorSpy: DisplayLinkCoordinator {
func add(_ participant: DisplayLinkParticipant) {}
func remove(_ participant: DisplayLinkParticipant) {}
}

private final class AnnotationImagesManagerSpy: AnnotationImagesManagerProtocol {
func addImage(_ image: UIImage, id: String, sdf: Bool, contentInsets: UIEdgeInsets) {}
func removeImage(_ imageName: String) {}
func register(imagesConsumer: MapboxMaps.AnnotationImagesConsumer) {}
func unregister(imagesConsumer: MapboxMaps.AnnotationImagesConsumer) {}
}

override func setUp() {
navigator = Navigator.shared
super.setUp()
navigationMapView = NavigationMapView(frame: CGRect(origin: .zero, size: .iPhone6Plus))
let mapboxMap = navigationMapView.mapView.mapboxMap!
pointAnnotationManager = PointAnnotationManager(id: "",
style: mapboxMap.style,
layerPosition: nil,
displayLinkCoordinator: DisplayLinkCoordinatorSpy(),
imagesManager: AnnotationImagesManagerSpy(),
offsetPointCalculator: OffsetPointCalculator(mapboxMap: mapboxMap))
}

override func tearDown() {
Expand Down Expand Up @@ -587,6 +611,82 @@ class NavigationMapViewTests: TestCase {
XCTAssertFalse(style.layerExists(withId: NavigationMapView.LayerIdentifier.intersectionAnnotationsLayer))
}

func testAddFinalDestinationWithDefaultIdentifierIfStyleNotLoaded() {
XCTAssertTrue(navigationMapView.finalDestinationAnnotations.isEmpty)
XCTAssertTrue(pointAnnotationManager.annotations.isEmpty)

navigationMapView.addDestinationAnnotation(coordinate1)
XCTAssertEqual(navigationMapView.finalDestinationAnnotations.count, 1)
XCTAssertEqual(navigationMapView.finalDestinationAnnotations[0].id, "\(finalDestinationAnnotationTestPrefix)_0")
XCTAssertTrue(pointAnnotationManager.annotations.isEmpty)

navigationMapView.addDestinationAnnotation(coordinate2)
XCTAssertEqual(navigationMapView.finalDestinationAnnotations.count, 2)
XCTAssertEqual(navigationMapView.finalDestinationAnnotations[1].id, "\(finalDestinationAnnotationTestPrefix)_1")
XCTAssertTrue(pointAnnotationManager.annotations.isEmpty)
}

func testAddFinalDestinationWithCustomIdentifierIfStyleNotLoaded() {
navigationMapView.addDestinationAnnotation(coordinate1, identifier: "custom")
XCTAssertEqual(navigationMapView.finalDestinationAnnotations.count, 1)
XCTAssertEqual(navigationMapView.finalDestinationAnnotations[0].id, "custom")
}

func testRemovesFinalDestinationIfStyleNotLoaded() {
navigationMapView.addDestinationAnnotation(coordinate1)
navigationMapView.addDestinationAnnotation(coordinate2)

navigationMapView.removeDestinationAnnotation(identifier: "custom")
XCTAssertEqual(navigationMapView.finalDestinationAnnotations.count, 2)

navigationMapView.addDestinationAnnotation(coordinate2, identifier: "custom")
XCTAssertEqual(navigationMapView.finalDestinationAnnotations.count, 3)
navigationMapView.removeDestinationAnnotation(identifier: "custom")
XCTAssertEqual(navigationMapView.finalDestinationAnnotations.count, 2)

navigationMapView.removeDestinationAnnotation()
XCTAssertEqual(navigationMapView.finalDestinationAnnotations.count, 0)
}

func testAddFinalDestinationWithDefaultIdentifierIfStyleLoaded() {
navigationMapView.pointAnnotationManager = pointAnnotationManager

navigationMapView.addDestinationAnnotation(coordinate1)
XCTAssertTrue(navigationMapView.finalDestinationAnnotations.isEmpty)
XCTAssertEqual(pointAnnotationManager.annotations.count, 1)
XCTAssertEqual(pointAnnotationManager.annotations[0].id, "\(finalDestinationAnnotationTestPrefix)_0")

navigationMapView.addDestinationAnnotation(coordinate2)
XCTAssertTrue(navigationMapView.finalDestinationAnnotations.isEmpty)
XCTAssertEqual(pointAnnotationManager.annotations.count, 2)
XCTAssertEqual(pointAnnotationManager.annotations[1].id, "\(finalDestinationAnnotationTestPrefix)_1")
}

func testAddFinalDestinationWithCustomIdentifierIfStyleLoaded() {
navigationMapView.pointAnnotationManager = pointAnnotationManager

navigationMapView.addDestinationAnnotation(coordinate1, identifier: "custom")
XCTAssertEqual(pointAnnotationManager.annotations.count, 1)
XCTAssertEqual(pointAnnotationManager.annotations[0].id, "custom")
}

func testRemovesFinalDestinationIfStyleLoaded() {
navigationMapView.pointAnnotationManager = pointAnnotationManager
navigationMapView.addDestinationAnnotation(coordinate1)
navigationMapView.addDestinationAnnotation(coordinate2)

navigationMapView.removeDestinationAnnotation(identifier: "custom")
XCTAssertEqual(pointAnnotationManager.annotations.count, 2)

navigationMapView.addDestinationAnnotation(coordinate2, identifier: "custom")
XCTAssertEqual(pointAnnotationManager.annotations.count, 3)
navigationMapView.removeDestinationAnnotation(identifier: "custom")
XCTAssertEqual(pointAnnotationManager.annotations.count, 2)

navigationMapView.removeDestinationAnnotation()
XCTAssertEqual(pointAnnotationManager.annotations.count, 0)
}

private func configureIntersections() {
let style = navigationMapView.mapView.mapboxMap.style
var source = GeoJSONSource()
Expand Down