Skip to content

Commit cfc2e3a

Browse files
committed
Expose Placemark.properties
Also refactored GeocodedPlacemark out of Placemark to improve readability.
1 parent 3fcfd49 commit cfc2e3a

File tree

5 files changed

+291
-202
lines changed

5 files changed

+291
-202
lines changed

MapboxGeocoder.xcodeproj/project.pbxproj

+18
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@
4646
35506B8B200F856400629509 /* BridgingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35506B8A200F856400629509 /* BridgingTests.m */; };
4747
35506B8C200F856400629509 /* BridgingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35506B8A200F856400629509 /* BridgingTests.m */; };
4848
35506B8D200F856400629509 /* BridgingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35506B8A200F856400629509 /* BridgingTests.m */; };
49+
355A7B7C21527B700001D2AD /* MBGeocodedPlacemark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 355A7B7B21527B700001D2AD /* MBGeocodedPlacemark.swift */; };
50+
355A7B7D21527B700001D2AD /* MBGeocodedPlacemark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 355A7B7B21527B700001D2AD /* MBGeocodedPlacemark.swift */; };
51+
355A7B7E21527B700001D2AD /* MBGeocodedPlacemark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 355A7B7B21527B700001D2AD /* MBGeocodedPlacemark.swift */; };
52+
355A7B7F21527B700001D2AD /* MBGeocodedPlacemark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 355A7B7B21527B700001D2AD /* MBGeocodedPlacemark.swift */; };
53+
355A7B8121527FAF0001D2AD /* forward_valid_hk.json in Resources */ = {isa = PBXBuildFile; fileRef = 355A7B8021527FAF0001D2AD /* forward_valid_hk.json */; };
54+
355A7B8221527FAF0001D2AD /* forward_valid_hk.json in Resources */ = {isa = PBXBuildFile; fileRef = 355A7B8021527FAF0001D2AD /* forward_valid_hk.json */; };
55+
355A7B8321527FAF0001D2AD /* forward_valid_hk.json in Resources */ = {isa = PBXBuildFile; fileRef = 355A7B8021527FAF0001D2AD /* forward_valid_hk.json */; };
4956
357B4358202CC90A00735521 /* reverse_address.json in Resources */ = {isa = PBXBuildFile; fileRef = 357B4357202CC90A00735521 /* reverse_address.json */; };
5057
357B4359202CC90A00735521 /* reverse_address.json in Resources */ = {isa = PBXBuildFile; fileRef = 357B4357202CC90A00735521 /* reverse_address.json */; };
5158
357B435A202CC90A00735521 /* reverse_address.json in Resources */ = {isa = PBXBuildFile; fileRef = 357B4357202CC90A00735521 /* reverse_address.json */; };
@@ -223,6 +230,8 @@
223230
07CF85C220F6DF93007B26B6 /* permanent_reverse_multiple_no_results.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = permanent_reverse_multiple_no_results.json; sourceTree = "<group>"; };
224231
07CF85C620F6DFEB007B26B6 /* permanent_reverse_single_valid.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = permanent_reverse_single_valid.json; sourceTree = "<group>"; };
225232
35506B8A200F856400629509 /* BridgingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BridgingTests.m; sourceTree = "<group>"; };
233+
355A7B7B21527B700001D2AD /* MBGeocodedPlacemark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MBGeocodedPlacemark.swift; sourceTree = "<group>"; };
234+
355A7B8021527FAF0001D2AD /* forward_valid_hk.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = forward_valid_hk.json; sourceTree = "<group>"; };
226235
357B4357202CC90A00735521 /* reverse_address.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = reverse_address.json; sourceTree = "<group>"; };
227236
35D3DE382112410A00B62912 /* CoreLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreLocation.swift; sourceTree = "<group>"; };
228237
DA1AC0211E5C23B8006DF1D6 /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; };
@@ -415,6 +424,7 @@
415424
DA2EC05D1CED732F00D4BA5D /* MBGeocodeOptions.swift */,
416425
DDC229591A36073B006BE405 /* MBGeocoder.swift */,
417426
DA2E03EF1CB0FDB400D1269A /* MBPlacemark.swift */,
427+
355A7B7B21527B700001D2AD /* MBGeocodedPlacemark.swift */,
418428
DA29C8DE1CEBE90200E48A61 /* MBPlacemarkScope.h */,
419429
DA2EC05B1CED72E900D4BA5D /* MBPlacemarkScope.swift */,
420430
DA2E03F11CB0FE0200D1269A /* MBRectangularRegion.swift */,
@@ -488,6 +498,7 @@
488498
DDF1E85B1BD70E4C00C40C78 /* reverse_valid.json */,
489499
DDF1E85A1BD70E4C00C40C78 /* reverse_invalid.json */,
490500
357B4357202CC90A00735521 /* reverse_address.json */,
501+
355A7B8021527FAF0001D2AD /* forward_valid_hk.json */,
491502
);
492503
name = Fixtures;
493504
path = fixtures;
@@ -793,6 +804,7 @@
793804
buildActionMask = 2147483647;
794805
files = (
795806
07CF85B020F6DF2F007B26B6 /* permanent_forward_multiple_no_results.json in Resources */,
807+
355A7B8221527FAF0001D2AD /* forward_valid_hk.json in Resources */,
796808
07CF85A820F6DF0F007B26B6 /* permanent_invalid.json in Resources */,
797809
07CF85C020F6DF86007B26B6 /* permanent_reverse_single_no_results.json in Resources */,
798810
DA5170B81CF1B1F900CD6DCF /* forward_valid.json in Resources */,
@@ -824,6 +836,7 @@
824836
buildActionMask = 2147483647;
825837
files = (
826838
07CF85B120F6DF2F007B26B6 /* permanent_forward_multiple_no_results.json in Resources */,
839+
355A7B8321527FAF0001D2AD /* forward_valid_hk.json in Resources */,
827840
07CF85A920F6DF0F007B26B6 /* permanent_invalid.json in Resources */,
828841
07CF85C120F6DF86007B26B6 /* permanent_reverse_single_no_results.json in Resources */,
829842
DA5170E21CF2542B00CD6DCF /* forward_valid.json in Resources */,
@@ -878,6 +891,7 @@
878891
buildActionMask = 2147483647;
879892
files = (
880893
07CF85AF20F6DF2F007B26B6 /* permanent_forward_multiple_no_results.json in Resources */,
894+
355A7B8121527FAF0001D2AD /* forward_valid_hk.json in Resources */,
881895
07CF85A720F6DF0F007B26B6 /* permanent_invalid.json in Resources */,
882896
07CF85BF20F6DF86007B26B6 /* permanent_reverse_single_no_results.json in Resources */,
883897
DA210BAD1CB4BFF7008088FD /* forward_valid.json in Resources */,
@@ -970,6 +984,7 @@
970984
files = (
971985
DA5170B31CF1B1E000CD6DCF /* MBRectangularRegion.swift in Sources */,
972986
DA5170B01CF1B1E000CD6DCF /* MBPlacemark.swift in Sources */,
987+
355A7B7D21527B700001D2AD /* MBGeocodedPlacemark.swift in Sources */,
973988
DA5170AF1CF1B1E000CD6DCF /* MBGeocoder.swift in Sources */,
974989
DA5170B21CF1B1E000CD6DCF /* MBPlacemarkScope.swift in Sources */,
975990
DA5170AE1CF1B1E000CD6DCF /* MBGeocodeOptions.swift in Sources */,
@@ -996,6 +1011,7 @@
9961011
files = (
9971012
DA5170DE1CF2541C00CD6DCF /* MBRectangularRegion.swift in Sources */,
9981013
DA5170DB1CF2541C00CD6DCF /* MBPlacemark.swift in Sources */,
1014+
355A7B7E21527B700001D2AD /* MBGeocodedPlacemark.swift in Sources */,
9991015
DA5170DA1CF2541C00CD6DCF /* MBGeocoder.swift in Sources */,
10001016
DA5170DD1CF2541C00CD6DCF /* MBPlacemarkScope.swift in Sources */,
10011017
DA5170D91CF2541C00CD6DCF /* MBGeocodeOptions.swift in Sources */,
@@ -1022,6 +1038,7 @@
10221038
files = (
10231039
DA5170F91CF2582F00CD6DCF /* MBRectangularRegion.swift in Sources */,
10241040
DA5170F61CF2582F00CD6DCF /* MBPlacemark.swift in Sources */,
1041+
355A7B7F21527B700001D2AD /* MBGeocodedPlacemark.swift in Sources */,
10251042
DA5170F51CF2582F00CD6DCF /* MBGeocoder.swift in Sources */,
10261043
DA5170F81CF2582F00CD6DCF /* MBPlacemarkScope.swift in Sources */,
10271044
DA5170F41CF2582F00CD6DCF /* MBGeocodeOptions.swift in Sources */,
@@ -1044,6 +1061,7 @@
10441061
files = (
10451062
DA2EC05E1CED732F00D4BA5D /* MBGeocodeOptions.swift in Sources */,
10461063
DA2E03F21CB0FE0200D1269A /* MBRectangularRegion.swift in Sources */,
1064+
355A7B7C21527B700001D2AD /* MBGeocodedPlacemark.swift in Sources */,
10471065
DDC2295E1A360843006BE405 /* MBGeocoder.swift in Sources */,
10481066
DA2EC05C1CED72E900D4BA5D /* MBPlacemarkScope.swift in Sources */,
10491067
DA2E03F01CB0FDB400D1269A /* MBPlacemark.swift in Sources */,
+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import Foundation
2+
3+
4+
/**
5+
A concrete subclass of `Placemark` to represent results of geocoding requests.
6+
*/
7+
@objc(MBGeocodedPlacemark)
8+
open class GeocodedPlacemark: Placemark {
9+
10+
private enum CodingKeys: String, CodingKey {
11+
case routableLocations = "routable_points"
12+
case relevance
13+
}
14+
15+
private enum PointsCodingKeys: String, CodingKey {
16+
case points
17+
}
18+
19+
/**
20+
An array of locations that serve as hints for navigating to the placemark.
21+
22+
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`.
23+
24+
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.
25+
*/
26+
@objc open var routableLocations: [CLLocation]?
27+
28+
public required init(from decoder: Decoder) throws {
29+
let container = try decoder.container(keyedBy: CodingKeys.self)
30+
31+
if let pointsContainer = try? container.nestedContainer(keyedBy: PointsCodingKeys.self, forKey: .routableLocations),
32+
var coordinatesContainer = try? pointsContainer.nestedUnkeyedContainer(forKey: .points) {
33+
34+
if let routableLocation = try coordinatesContainer.decodeIfPresent(RoutableLocation.self),
35+
let coordinate = routableLocation.coordinate {
36+
routableLocations = [CLLocation(coordinate: coordinate)]
37+
}
38+
}
39+
40+
relevance = try container.decodeIfPresent(Double.self, forKey: .relevance) ?? -1
41+
42+
try super.init(from: decoder)
43+
}
44+
45+
public override func encode(to encoder: Encoder) throws {
46+
var container = encoder.container(keyedBy: CodingKeys.self)
47+
48+
try container.encodeIfPresent(relevance, forKey: .relevance)
49+
50+
if let routableLocations = routableLocations,
51+
!routableLocations.isEmpty {
52+
var pointsContainer = container.nestedContainer(keyedBy: PointsCodingKeys.self, forKey: .routableLocations)
53+
var coordinatesContainer = pointsContainer.nestedUnkeyedContainer(forKey: .points)
54+
let routableLocation = RoutableLocation(coordinates: [routableLocations[0].coordinate.longitude,
55+
routableLocations[0].coordinate.latitude])
56+
try coordinatesContainer.encode(routableLocation)
57+
}
58+
59+
try super.encode(to: encoder)
60+
}
61+
62+
@objc open override var debugDescription: String {
63+
return qualifiedName!
64+
}
65+
66+
internal var qualifiedNameComponents: [String] {
67+
if qualifiedName!.contains(", ") {
68+
return qualifiedName!.components(separatedBy: ", ")
69+
}
70+
// Chinese addresses have no commas and are reversed.
71+
return (superiorPlacemarks?.map { $0.name } ?? []).reversed() + [name]
72+
}
73+
74+
@objc open var formattedName: String {
75+
let text = super.name
76+
// 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.
77+
if let houseNumber = address, scope == .address {
78+
let streetName = text
79+
let reversedAddress = "\(streetName) \(houseNumber)"
80+
if qualifiedNameComponents.contains(reversedAddress) {
81+
return reversedAddress
82+
} else {
83+
return "\(houseNumber) \(streetName)"
84+
}
85+
} else {
86+
return text
87+
}
88+
}
89+
90+
@objc open override var genres: [String]? {
91+
return properties?.category?.components(separatedBy: ", ")
92+
}
93+
94+
@objc open override var imageName: String? {
95+
return properties?.maki
96+
}
97+
98+
/**
99+
A numerical score from 0 (least relevant) to 0.99 (most relevant) measuring
100+
how well each returned feature matches the query. Use this property to
101+
remove results that don’t fully match the query.
102+
*/
103+
@objc open var relevance: Double
104+
105+
private var clippedAddressLines: [String] {
106+
let lines = qualifiedNameComponents
107+
if scope == .address {
108+
return lines
109+
}
110+
111+
guard let qualifiedName = qualifiedName,
112+
qualifiedName.contains(", ") else {
113+
// Chinese addresses have no commas and are reversed.
114+
return Array(lines.prefix(lines.count))
115+
}
116+
117+
return Array(lines.suffix(from: 1))
118+
}
119+
120+
/**
121+
The placemark’s full address in the customary local format, with each line in a separate string in the array.
122+
123+
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.
124+
*/
125+
var formattedAddressLines: [String]? {
126+
return clippedAddressLines
127+
}
128+
129+
#if !os(tvOS)
130+
@available(iOS 9.0, OSX 10.11, *)
131+
@objc open override var postalAddress: CNPostalAddress? {
132+
let postalAddress = CNMutablePostalAddress()
133+
134+
if scope == .address {
135+
postalAddress.street = name
136+
} else if let address = address {
137+
postalAddress.street = address.replacingOccurrences(of: ", ", with: "\n")
138+
}
139+
140+
if let placeName = place?.name {
141+
postalAddress.city = placeName
142+
}
143+
if let regionName = administrativeRegion?.name {
144+
postalAddress.state = regionName
145+
}
146+
if let postalCode = postalCode?.name {
147+
postalAddress.postalCode = postalCode
148+
}
149+
if let countryName = country?.name {
150+
postalAddress.country = countryName
151+
}
152+
if let ISOCountryCode = country?.code {
153+
postalAddress.isoCountryCode = ISOCountryCode
154+
}
155+
156+
return postalAddress
157+
}
158+
#endif
159+
160+
open override var code: String? {
161+
get {
162+
return country?.code
163+
}
164+
set {
165+
country?.code = code
166+
}
167+
}
168+
169+
@objc open override var addressDictionary: [AnyHashable: Any]? {
170+
var addressDictionary: [String: Any] = [:]
171+
if scope == .address {
172+
addressDictionary[MBPostalAddressStreetKey] = name
173+
} else if let address = properties?.address {
174+
addressDictionary[MBPostalAddressStreetKey] = address
175+
} else if let address = address {
176+
addressDictionary[MBPostalAddressStreetKey] = address
177+
}
178+
addressDictionary[MBPostalAddressCityKey] = place?.name
179+
addressDictionary[MBPostalAddressStateKey] = administrativeRegion?.name
180+
addressDictionary[MBPostalAddressPostalCodeKey] = postalCode?.name
181+
addressDictionary[MBPostalAddressCountryKey] = country?.name
182+
addressDictionary[MBPostalAddressISOCountryCodeKey] = country?.code
183+
addressDictionary["formattedAddressLines"] = clippedAddressLines
184+
addressDictionary["name"] = name
185+
addressDictionary["subAdministrativeArea"] = district?.name ?? place?.name
186+
addressDictionary["subLocality"] = neighborhood?.name
187+
addressDictionary["subThoroughfare"] = subThoroughfare
188+
addressDictionary["thoroughfare"] = thoroughfare
189+
return addressDictionary
190+
}
191+
192+
/**
193+
The phone number to contact a business at this location.
194+
*/
195+
@objc open override var phoneNumber: String? {
196+
return properties?.phoneNumber
197+
}
198+
}

0 commit comments

Comments
 (0)