-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathMBGeocodedPlacemark.swift
More file actions
194 lines (160 loc) · 7.83 KB
/
MBGeocodedPlacemark.swift
File metadata and controls
194 lines (160 loc) · 7.83 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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
import Foundation
/**
A concrete subclass of `Placemark` to represent results of geocoding requests.
*/
@objc(MBGeocodedPlacemark)
open class GeocodedPlacemark: Placemark {
private enum CodingKeys: String, CodingKey {
case routableLocations = "routable_points"
case relevance
}
private enum PointsCodingKeys: String, CodingKey {
case points
}
/**
An array of locations that serve as hints for navigating to the placemark.
If the `GeocodeOptions.includesRoutableLocations` property is set to `true`, this property contains locations that are suitable to use as a waypoint in a routing engine such as MapboxDirections.swift. Otherwise, if the `GeocodeOptions.includesRoutableLocations` property is set to `false`, this property is set to `nil`.
For the placemark’s geographic center, use the `location` property. The routable locations may differ from the geographic center. For example, if a house’s driveway leads to a street other than the nearest street (by straight-line distance), then this property may contain the location where the driveway meets the street. A route to the placemark’s geographic center may be impassable, but a route to the routable location would end on the correct street with access to the house.
*/
@objc open var routableLocations: [CLLocation]?
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let pointsContainer = try? container.nestedContainer(keyedBy: PointsCodingKeys.self, forKey: .routableLocations),
var coordinatesContainer = try? pointsContainer.nestedUnkeyedContainer(forKey: .points) {
if let routableLocation = try coordinatesContainer.decodeIfPresent(RoutableLocation.self),
let coordinate = routableLocation.coordinate {
routableLocations = [CLLocation(coordinate: coordinate)]
}
}
relevance = try container.decodeIfPresent(Double.self, forKey: .relevance) ?? -1
try super.init(from: decoder)
}
public override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(relevance, forKey: .relevance)
if let routableLocations = routableLocations,
!routableLocations.isEmpty {
var pointsContainer = container.nestedContainer(keyedBy: PointsCodingKeys.self, forKey: .routableLocations)
var coordinatesContainer = pointsContainer.nestedUnkeyedContainer(forKey: .points)
let routableLocation = RoutableLocation(coordinates: [routableLocations[0].coordinate.longitude,
routableLocations[0].coordinate.latitude])
try coordinatesContainer.encode(routableLocation)
}
try super.encode(to: encoder)
}
@objc open override var debugDescription: String {
return qualifiedName!
}
internal var qualifiedNameComponents: [String] {
if qualifiedName!.contains(", ") {
return qualifiedName!.components(separatedBy: ", ")
}
// Chinese addresses have no commas and are reversed.
return (superiorPlacemarks?.map { $0.name } ?? []).reversed() + [name]
}
@objc open var formattedName: String {
let text = super.name
// For address features, `text` is just the street name. Look through the fully-qualified address to determine whether to put the house number before or after the street name.
if let houseNumber = address, scope == .address {
let streetName = text
let reversedAddress = "\(streetName) \(houseNumber)"
if qualifiedNameComponents.contains(reversedAddress) {
return reversedAddress
} else {
return "\(houseNumber) \(streetName)"
}
} else {
return text
}
}
@objc open override var genres: [String]? {
return properties?.category?.components(separatedBy: ", ")
}
@objc open override var imageName: String? {
return properties?.maki
}
/**
A numerical score from 0 (least relevant) to 0.99 (most relevant) measuring
how well each returned feature matches the query. Use this property to
remove results that don’t fully match the query.
*/
@objc open var relevance: Double
private var clippedAddressLines: [String] {
let lines = qualifiedNameComponents
if scope == .address {
return lines
}
guard let qualifiedName = qualifiedName,
qualifiedName.contains(", ") else {
// Chinese addresses have no commas and are reversed.
return Array(lines.prefix(lines.count))
}
return Array(lines.suffix(from: 1))
}
/**
The placemark’s full address in the customary local format, with each line in a separate string in the array.
If you need to fit the same address on a single line, use the `qualifiedName` property, in which each line is separated by a comma instead of a line break.
*/
var formattedAddressLines: [String]? {
return clippedAddressLines
}
#if !os(tvOS)
@available(iOS 9.0, OSX 10.11, *)
@objc open override var postalAddress: CNPostalAddress? {
let postalAddress = CNMutablePostalAddress()
if scope == .address {
postalAddress.street = name
} else if let address = address {
postalAddress.street = address.replacingOccurrences(of: ", ", with: "\n")
}
if let placeName = place?.name {
postalAddress.city = placeName
}
if let regionName = administrativeRegion?.name {
postalAddress.state = regionName
}
if let postalCode = postalCode?.name {
postalAddress.postalCode = postalCode
}
if let countryName = country?.name {
postalAddress.country = countryName
}
if let ISOCountryCode = country?.code {
postalAddress.isoCountryCode = ISOCountryCode
}
return postalAddress
}
#endif
open override var code: String? {
get { return country?.code }
set { country?.code = code }
}
@objc open override var addressDictionary: [AnyHashable: Any]? {
var addressDictionary: [String: Any] = [:]
if scope == .address {
addressDictionary[MBPostalAddressStreetKey] = name
} else if let address = properties?.address {
addressDictionary[MBPostalAddressStreetKey] = address
} else if let address = address {
addressDictionary[MBPostalAddressStreetKey] = address
}
addressDictionary[MBPostalAddressCityKey] = place?.name
addressDictionary[MBPostalAddressStateKey] = administrativeRegion?.name
addressDictionary[MBPostalAddressPostalCodeKey] = postalCode?.name
addressDictionary[MBPostalAddressCountryKey] = country?.name
addressDictionary[MBPostalAddressISOCountryCodeKey] = country?.code
addressDictionary["formattedAddressLines"] = clippedAddressLines
addressDictionary["name"] = name
addressDictionary["subAdministrativeArea"] = district?.name ?? place?.name
addressDictionary["subLocality"] = neighborhood?.name
addressDictionary["subThoroughfare"] = subThoroughfare
addressDictionary["thoroughfare"] = thoroughfare
return addressDictionary
}
/**
The phone number to contact a business at this location.
*/
@objc open override var phoneNumber: String? {
return properties?.phoneNumber
}
}