In one of my apps I need to add an ability to find a city by its name. I am using CLGeocoder
to achieve this and I want it to have a behaviour identical to iOS weather app.
Below is the code which I have:
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)")
}
})
It works very well in most cases. However, I recently noticed some cases when it fails. Below is the output for some examples:
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])
This is the correct output
There is a town in Texas called Italy. If I type 'Italy, TX' the output is:
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])
When I type just 'Italy' I only get place mark for the country:
locality: nil
name: Optional("Italy")
country: Optional("Italy")
formatted address: Optional([CountryCode: IT, Name: Italy, FormattedAddressLines: (
Italy
), Country: Italy])
This is similar to the case with '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])
Again, seems similar to the case with 'Italy'. It only finds a country:
locality: nil
name: Optional("Singapore")
country: Optional("Singapore")
formatted address: Optional([CountryCode: SG, Name: Singapore, FormattedAddressLines: (
Singapore
), Country: Singapore])
At this stage I though that maybe geocodeAddressString:
stops searching if it finds a country with the name equal to the search parameter, so I tried changing my code to :
let addrDict = [kABPersonAddressCityKey as NSString: searchBar.text!]
CLGeocoder().geocodeAddressDictionary(addrDict, completionHandler:{ (placemarks, error) -> Void in
print(placemarks)
})
I thought that by restricting the search term to the city name I would get correct results. Unfortunately, I get the exact same behaviour!
And THEN I realised that I have problems not only with cases when city name is equal to the country name.
locality: nil
name: Optional("Tokyo")
country: Optional("Japan")
formatted address: Optional([CountryCode: JP, Name: Tokyo, State: Tokyo, FormattedAddressLines: (
Tokyo,
Japan
), Country: Japan])
Not only can CLGeocoder
omit results (like in the case of 'Italy') but it can also tell me that a place does not have a locality
when, in fact, it does (I believe last time I checked a map Tokyo was still a city!).
Does anyone know how I can resolve any of these issues?
Weather app does it somehow - searching for 'Singapore' or 'Italy' there gives correct results. I will be grateful for any help!
P.S. Although I am using Swift I am adding Objective-C as a tag as I think that this question is not Swift specific.
Usage what you showed is completely right and I don't think there is anything else you can do to improve results. It is still just some complicated algorithm that tries to sort results based on set of rules and some assumption.
That being sad, I understand your frustration completely because I've been there and done that. The problem is that Geocoding database is not obviously complete and Apple is not the company who leads this field - Google is. Here you can see technical note from when the Geocoder was introduced saying that there are still countries that are not supported, or just partially supported.
So my suggestion to you is that if you want to get best results (though still not fail-proof), switch to Google Geolocation. For quite great amount of requests it is free and after that it costs some very minor amounts. The success rate is around 99% for all the basic searches from what I experienced and it only gets better if you provide more information. The API options are also quite extensive.
Here are links for developer documentation for both Geocoding and Reverse Geocoding: https://developers.google.com/maps/documentation/geocoding/intro https://developers.google.com/maps/documentation/javascript/examples/geocoding-reverse
Hope it helped at least a little.
EDIT: After I wrote this I did a little research and it seems that I am not the only person to make this claim (also including the same unsupported link).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With