当城市名称与某些国家的名称相同时(不仅仅是这种情况),CLGeocoder返回错误结果。

16
在我的某个应用程序中,我需要添加一项按名称查找城市的功能。我正在使用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' - 成功

Texas州有一个名叫Italy的城镇。如果我输入 '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])

搜索'Singapore, Singapore' - 已找到

这与 '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。


请问,您已经使用任何解决方案解决了问题吗? - Jiri Trecak
4个回答

7

你展示的用法完全正确,我认为没有其他方法可以改进结果。它仍然只是一个尝试基于一组规则和一些假设对结果进行排序的复杂算法。

话虽如此,我完全理解你的沮丧,因为我也经历过这种情况。问题在于地理编码数据库显然不完整,苹果公司不是领导这个领域的公司 - 谷歌是。在这里,你可以看到地理编码器被介绍时的技术说明,其中声明仍有一些国家不支持,或者仅部分支持。

因此,我建议你如果想获得最好的结果(尽管仍然不是万无一失),转换为谷歌地理定位。对于相当大量的请求,它是免费的,之后只会花费一些非常小的金额。从我的经验来看,所有基本搜索的成功率约为99%,如果提供更多信息,则只会变得更好。API选项也非常广泛。

以下是地理编码和反向地理编码的开发人员文档链接: https://developers.google.com/maps/documentation/geocoding/intro https://developers.google.com/maps/documentation/javascript/examples/geocoding-reverse

希望它至少有点帮助。

编辑:我写完这篇文章后,做了一些研究,发现我并不是唯一一个提出这种观点的人(还包括相同的不支持链接)。


太好了,这很有效!不过我现在遇到了另一个困难,因为我需要在收到地理编码API的响应后调用时区API。如果您有任何关于如何最好地完成它的想法,请查看我的新问题http://stackoverflow.com/questions/32144089/alamofire-with-google-geocoding-api - Andriy Gordiychuk

2

CLGeocoder的请求受到限制,如果应用程序超过阈值,进一步的请求可能无法被满足。CLGeocoder需要网络连接,在某些情况下可能会受到限制,而来自服务器的响应并不总是即时的。如果您不坚持使用CLGeocoder,则本地数据库可能会为您提供更多的灵活性和可能更好的用户体验。

GeoNames上查看cities???.zip,这是一个替代方案,您可以将数据导入到本地数据库中。


这如何解释东京的情况? - Andriy Gordiychuk

2
你可以通过 Google 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)

}
}

}

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。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接