CLGeocoder возвращает неверные результаты, когда название города равно имени какой-либо страны (и не только)

В одном из моих приложений мне нужно добавить возможность найти город по его названию. Я использую 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])

Это правильный вывод

Поиск "Италия, TX" - РАБОТЫ

В Техасе есть город, называемый Италией. Если я нахожу "Италия, 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])

Поиск "Италия" - FAILS

Когда я печатаю только "Италию", я получаю только местную отметку для страны:

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])

Поиск "Сингапур" - FAILS

Опять же, похоже на случай с "Италией". Он находит только страну:

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)
})

Я думал, что, ограничив поисковый термин на название города, я получу правильные результаты. К сожалению, я получаю то же самое поведение!

И THEN Я понял, что у меня проблемы с не только случаем, когда название города равно имени страны.

Поиск "Токио" - FAIC FAIL

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.

Ответ 1

Использование того, что вы показали, совершенно правильно, и я не думаю, что вы можете сделать что-то еще, чтобы улучшить результаты. Это еще какой-то сложный алгоритм, который пытается сортировать результаты на основе набора правил и некоторых предположений.

Что грустно, я полностью понимаю ваше разочарование, потому что я был там и сделал это. Проблема в том, что база данных Geocoding явно не завершена, и Apple не является компанией, которая ведет это поле - Google. Здесь вы можете увидеть техническую ноту, когда был введен Geocoder, в котором говорилось, что существуют страны, которые не поддерживаются или частично поддерживаются.

Итак, мое предложение для вас состоит в том, что если вы хотите получить лучшие результаты (хотя все еще не поддаются проверке), переключитесь на Google Geolocation. По довольно большому количеству запросов он бесплатный, и после этого он стоит очень незначительные суммы. Уровень успеха составляет около 99% для всех основных поисков из того, что я испытал, и он становится лучше, если вы предоставляете больше информации. Параметры API также довольно обширны.

Вот ссылки для документации для разработчиков как для геокодирования, так и для обратного геокодирования: https://developers.google.com/maps/documentation/geocoding/intro https://developers.google.com/maps/documentation/javascript/examples/geocoding-reverse

Надеюсь, это помогло хотя бы немного.

EDIT: после того, как я написал это, я сделал небольшое исследование, и кажется, что я не единственный человек сделать это выражение (также включая ту же неподдерживаемую ссылку).

Ответ 2

Запросы CLGeocoder ограничены по скорости, и если приложение переходит за порог, дальнейшие запросы могут не выполняться. CLGeocoder требует сетевого подключения, которое может быть ограничено при некоторых обстоятельствах, и ответ от сервера не всегда мгновен. Если вы не настаиваете на CLGeocoder, локальная база данных может дать вам больше гибкости и, возможно, улучшить работу пользователей.

Взгляните на города???. zip на GeoNames, для альтернативного решения, в котором вы импортируете данные в локальная база данных.

Ответ 3

Вы также можете сделать это с помощью API Google, как этот

//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)

}
}

}