-
Notifications
You must be signed in to change notification settings - Fork 325
Expand file tree
/
Copy pathNavigationMapView+RoadNameLabeling.swift
More file actions
174 lines (144 loc) · 8.41 KB
/
Copy pathNavigationMapView+RoadNameLabeling.swift
File metadata and controls
174 lines (144 loc) · 8.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import MapboxMaps
import MapboxDirections
import MapboxCoreNavigation
import os.log
private let logger: OSLog = .init(subsystem: "com.mapbox.navigation", category: "RoadNameLabeling")
extension NavigationMapView {
func labelCurrentRoadFeature(at location: CLLocation,
router: Router,
wayNameView: WayNameView,
roadNameFromStatus: String?) {
guard let stepShape = router.routeProgress.currentLegProgress.currentStep.shape,
!stepShape.coordinates.isEmpty else {
return
}
// Add Mapbox Streets if the map does not already have it
if mapView.streetsSources().isEmpty {
var streetsSource = VectorSource()
streetsSource.url = "mapbox://mapbox.mapbox-streets-v8"
let sourceIdentifier = "com.mapbox.MapboxStreets"
do {
try mapView.mapboxMap.style.addSource(streetsSource, id: sourceIdentifier)
} catch {
os_log("Failed to add %s with error: %s.", log: logger, type: .error, sourceIdentifier as CVarArg, error.localizedDescription)
}
}
guard let mapboxStreetsSource = mapView.streetsSources().first else { return }
let identifierNamespace = Bundle.mapboxNavigation.bundleIdentifier ?? ""
let roadLabelStyleLayerIdentifier = "\(identifierNamespace).roadLabels"
let roadLabelLayer = try? mapView.mapboxMap.style.layer(withId: roadLabelStyleLayerIdentifier) as? LineLayer
if roadLabelLayer == nil {
var streetLabelLayer = LineLayer(id: roadLabelStyleLayerIdentifier)
streetLabelLayer.source = mapboxStreetsSource.id
var sourceLayerIdentifier: String? {
let identifiers = mapView.tileSetIdentifiers(mapboxStreetsSource.id,
sourceType: mapboxStreetsSource.type.rawValue)
if VectorSource.isMapboxStreets(identifiers) {
return identifiers.compactMap({ VectorSource.roadLabelLayerIdentifiersByTileSetIdentifier[$0] }).first
}
return nil
}
streetLabelLayer.sourceLayer = sourceLayerIdentifier
streetLabelLayer.lineOpacity = .constant(1.0)
streetLabelLayer.lineWidth = .constant(20.0)
streetLabelLayer.lineColor = .constant(.init(.white))
if ![ProfileIdentifier.walking, ProfileIdentifier.cycling].contains(router.routeProgress.routeOptions.profileIdentifier) {
// Filter out to road classes valid only for motor transport.
let filter = Exp(.inExpression) {
"class"
"motorway"
"motorway_link"
"trunk"
"trunk_link"
"primary"
"primary_link"
"secondary"
"secondary_link"
"tertiary"
"tertiary_link"
"street"
"street_limited"
"roundabout"
}
streetLabelLayer.filter = filter
}
do {
var layerPosition: MapboxMaps.LayerPosition? = nil
if let firstLayerIdentifier = mapView.mapboxMap.style.allLayerIdentifiers.first?.id {
layerPosition = .below(firstLayerIdentifier)
}
try mapView.mapboxMap.style.addLayer(streetLabelLayer, layerPosition: layerPosition)
} catch {
os_log("Failed to add %s with error: %s.", log: logger, type: .error, roadLabelStyleLayerIdentifier as CVarArg, error.localizedDescription)
}
}
let closestCoordinate = location.coordinate
let lookAheadDistance: CLLocationDistance = 10
let pointAheadUser = stepShape.sliced(from: closestCoordinate)?.coordinateFromStart(distance: lookAheadDistance)
let position = mapView.mapboxMap.point(for: closestCoordinate)
mapView.mapboxMap.queryRenderedFeatures(at: position,
options: RenderedQueryOptions(layerIds: [roadLabelStyleLayerIdentifier], filter: nil)) { [weak self] result in
switch result {
case .success(let queriedFeatures):
guard let self = self else { return }
var smallestLabelDistance = Double.infinity
var latestFeature: Turf.Feature?
var minimumEditDistance = Int.max
var similarFeature: Turf.Feature?
for queriedFeature in queriedFeatures {
// Calculate the Levenshtein–Damerau edit distance between the road name from status and the feature property road name, and then use the smallest one for the road label.
if case let .string(roadName) = queriedFeature.feature.properties?["name"],
let roadNameFromStatus = roadNameFromStatus {
let stringEditDistance = roadNameFromStatus.minimumEditDistance(to: roadName)
if stringEditDistance < minimumEditDistance {
minimumEditDistance = stringEditDistance
similarFeature = queriedFeature.feature
}
}
guard let pointAheadUser = pointAheadUser else { continue }
var lineStrings: [LineString] = []
switch queriedFeature.feature.geometry {
case .lineString(let lineString):
lineStrings.append(lineString)
case .multiLineString(let multiLineString):
for coordinates in multiLineString.coordinates {
lineStrings.append(LineString(coordinates))
}
default:
break
}
for lineString in lineStrings {
guard let pointAheadFeature = lineString.sliced(from: closestCoordinate)?.coordinateFromStart(distance: lookAheadDistance) else { continue }
guard let reversedPoint = LineString(lineString.coordinates.reversed()).sliced(from: closestCoordinate)?.coordinateFromStart(distance: lookAheadDistance) else { continue }
let distanceBetweenPointsAhead = pointAheadFeature.distance(to: pointAheadUser)
let distanceBetweenReversedPoint = reversedPoint.distance(to: pointAheadUser)
let minDistanceBetweenPoints = min(distanceBetweenPointsAhead, distanceBetweenReversedPoint)
if minDistanceBetweenPoints < smallestLabelDistance {
smallestLabelDistance = minDistanceBetweenPoints
latestFeature = queriedFeature.feature
}
}
}
var hideWayName = true
if latestFeature?.featureIdentifier != similarFeature?.featureIdentifier {
let style = self.mapView.mapboxMap.style
if let similarFeature = similarFeature,
wayNameView.setupWith(feature: similarFeature,
using: style) {
hideWayName = false
}
} else if smallestLabelDistance < 5 {
let style = self.mapView.mapboxMap.style
if let latestFeature = latestFeature,
wayNameView.setupWith(feature: latestFeature,
using: style) {
hideWayName = false
}
}
wayNameView.containerView.isHidden = hideWayName
case .failure:
os_log("Failed to find visible features.", log: logger, type: .error)
}
}
}
}