在我的某个应用中,我需要添加一个按名称查找城市的功能。我正在使用CLGeocoder
来实现此目标,我希望它具有与iOS天气应用相同的行为。
以下是我的代码:
CLGeocoder().geocodeAddressString(searchBar.text!, completionHandler:{ (placemarks, error) -> Void in
guard let nonNilMarks = placemarks else {return}
for placemark in nonNilMarks {
print("locality: \(placemark.locality)")
print("name: \(placemark.name)")
print("country: \(placemark.country)")
print("formatted address: \(placemark.addressDictionary)")
}
})
在大多数情况下,它的效果非常好。但是,我最近注意到一些失败的情况。以下是一些示例的输出:
locality: Optional("Milan")
name: Optional("Milan")
country: Optional("Italy")
formatted address: Optional([SubAdministrativeArea: Milan, State: Lombardy, CountryCode: IT, Country: Italy, Name: Milan, FormattedAddressLines: (
Milan,
Italy
), City: Milan])
这是正确的输出
德克萨斯州有一个叫意大利的小镇。如果我输入'Italy,TX',则输出为:
locality: Optional("Italy")
name: Optional("Italy")
country: Optional("United States")
formatted address: Optional([SubAdministrativeArea: Ellis, State: TX, CountryCode: US, Country: United States, Name: Italy, FormattedAddressLines: (
"Italy, TX",
"United States"
), City: Italy])
当我输入“意大利”时,我只获得该国家的地方标记:
locality: nil
name: Optional("Italy")
country: Optional("Italy")
formatted address: Optional([CountryCode: IT, Name: Italy, FormattedAddressLines: (
Italy
), Country: Italy])
这类似于'Italy,TX'的情况:
locality: Optional("Singapore")
name: Optional("Singapore")
country: Optional("Singapore")
formatted address: Optional([City: Singapore, CountryCode: SG, Name: Singapore, State: Singapore, FormattedAddressLines: (
Singapore,
Singapore
), Country: Singapore])
再次,似乎与'意大利'的情况类似。它只找到一个国家:
locality: nil
name: Optional("Singapore")
country: Optional("Singapore")
formatted address: Optional([CountryCode: SG, Name: Singapore, FormattedAddressLines: (
Singapore
), Country: Singapore])
在此阶段我可能会geocodeAddressString:
停止搜索是否找到名称等于搜索参数的国家/地区,因此我尝试将代码更改为:
let addrDict = [kABPersonAddressCityKey as NSString: searchBar.text!]
CLGeocoder().geocodeAddressDictionary(addrDict, completionHandler:{ (placemarks, error) -> Void in
print(placemarks)
})
我认为通过将搜索字词限制为城市名称,我会得到正确的结果。不幸的是,我得到了完全相同的行为!
然后那么我意识到我不仅遇到城市名称等于国名的情况。
locality: nil
name: Optional("Tokyo")
country: Optional("Japan")
formatted address: Optional([CountryCode: JP, Name: Tokyo, State: Tokyo, FormattedAddressLines: (
Tokyo,
Japan
), Country: Japan])
CLGeocoder
不仅可以省略结果(例如'意大利'),但它也可以告诉我一个地方没有locality
,事实上它确实如此(我相信上次我查了一张地图东京还是一个城市!)。
有谁知道如何解决这些问题?
天气应用程序以某种方式做到了 - 搜索“新加坡”或“意大利”会给出正确的结果。我将不胜感激任何帮助!
P.S。虽然我使用Swift,但我将Objective-C添加为标签,因为我认为这个问题不是特定于Swift的。
答案 0 :(得分:7)
使用您展示的内容是完全正确的,我不认为您还可以采取其他任何措施来改善效果。它仍然只是一些复杂的算法,试图根据规则集和一些假设对结果进行排序。
令人伤心的是,我完全理解你的挫败感,因为我已经去过那里并做到了。问题在于地理编码数据库显然并不完整,而苹果并不是领导这一领域的公司 - 谷歌就是。在这里,您可以看到从Geocoder介绍时说明there are still countries that are not supported或仅部分支持的技术说明。
所以我向你提出的建议是,如果你想获得最好的结果(虽然仍然不能防止失败),请切换到Google Geolocation。对于相当多的请求它是免费的,之后它需要一些非常小的金额。根据我的经验,所有基本搜索的成功率约为99%,如果您提供更多信息,它的效果会更好。 API选项也非常广泛。
以下是地理编码和反向地理编码的开发人员文档链接: https://developers.google.com/maps/documentation/geocoding/intro https://developers.google.com/maps/documentation/javascript/examples/geocoding-reverse
希望它至少有一点帮助。
编辑:在我写完之后,我做了一些研究,似乎我不是make this claim的唯一人(也包括相同的不受支持的链接)。答案 1 :(得分:2)
CLGeocoder请求受到速率限制,如果应用超过阈值,则可能无法满足其他请求。 CLGeocoder需要网络连接,这在某些情况下可能会受到限制,而服务器的响应并不总是即时的。如果您不坚持使用CLGeocoder,本地数据库可能会为您提供更大的灵活性和更好的用户体验。
查看GeoNames上的城市???。zip,以获取将数据导入本地数据库的替代解决方案。
答案 2 :(得分:2)
您也可以通过谷歌API这样做
//chakshu
var urlString = "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=\(searchString)&sensor=true&key=\(Google_Browser_Key)"
var linkUrl:NSURL = NSURL(string:urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)!
if(countElements(urlString)>0 && !urlString.isEmpty)
{
// if let fileData = String(contentsOfURL: NSURL(string: urlString)!, encoding: NSUTF8StringEncoding, error: nil)
if let fileData = String(contentsOfURL: linkUrl, encoding: NSUTF8StringEncoding, error: nil)
{
println(fileData)
var data = fileData.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
var localError: NSError?
if(data != nil)
{
var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &localError)
}
}
}
答案 3 :(得分:0)
在巴西,我也面临着这个问题,所以我为此做出了一个可怕的解决方案:
public func getLatLonFrom(address: String, completion:@escaping ((CLLocation?, NSError?)->Void)) {
//LCEssentials.printInfo(title: "LOCATION", msg: "CEP: \(address)")
let geoCoder = CLGeocoder()
let params: [String: Any] = [
CNPostalAddressPostalCodeKey: address,
CNPostalAddressISOCountryCodeKey: "BR"
]
geoCoder.geocodeAddressString(address) { (placemarks, error) in
//LCEssentials.printInfo(title: "LOCATION", msg: "PLACEMARKS FIRST: \(placemarks?.debugDescription)")
if let locais = placemarks {
guard let location = locais.first?.location else {
let error = NSError(domain: LCEssentials().DEFAULT_ERROR_DOMAIN, code: LCEssentials().DEFAULT_ERROR_CODE, userInfo: [ NSLocalizedDescriptionKey: "Address not found" ])
completion(nil, error)
return
}
completion(location, nil)
}else{
geoCoder.geocodeAddressDictionary(params) { (placemarks, error) in
//LCEssentials.printInfo(title: "LOCATION", msg: "PLACEMARKS: \(placemarks?.debugDescription)")
guard let founded = placemarks, let location = founded.first?.location else {
let error = NSError(domain: LCEssentials().DEFAULT_ERROR_DOMAIN, code: LCEssentials().DEFAULT_ERROR_CODE, userInfo: [ NSLocalizedDescriptionKey: "Address not found" ])
completion(nil, error)
return
}
completion(location, nil)
}
}
}
}
我将国家/地区代码强制设为BR(巴西),然后可以通过邮政编码找到地址。如果它在geocodeAddressString
上返回地标的NIL,则我致电geocodeAddressDictionary
,并收到正确的结果。
该解决方案可以涵盖BR的主要邮政编码。这是一个混乱的代码,但目前可以使用。
将来的项目中,我将使用Google Maps。