iOS - Get Address around a Long/Lat - iphone

I was wondering if anyone knew how to get a list of addresses around a longitude and latitude?
I've been using the following code but it always yields one address:
[self.geocoder reverseGeocodeLocation: currentLocation completionHandler:
^(NSArray *placemarks, NSError *error) {
for (int i = 0; i < placemarks.count; i++)
{
CLPlacemark *placemark = [placemarks objectAtIndex:i];
if (placemark.addressDictionary != nil)
{
Location *aLocation = [[Location alloc] init];
aLocation.locationName = [placemark.addressDictionary valueForKey:#"Name"];
aLocation.locationAddress = [placemark.addressDictionary valueForKey:#"City"];
aLocation.currentLocation = placemark.location;
[self.tableData addObject:aLocation];
}
}
[self.locationsTableView reloadData];
}];

I'm not sure if Core Location's geocoded is intended to do that.
Google allows for this kind of search but with limits with their Places API. That being said, you can do a search for something like "Pizza" and get results within a radius.
For more info look at: https://developers.google.com/places/documentation/
You can use their API to query based on a lat/long with a search query and radius to get results looking like what you're after.
Good luck!

First change your latitude and longitude value (and check may be your code is right) becoze in map, some coordinate does not provide full information.
following provide code for if some coordinate does not provide full information then how can give you condition for get specific CITY(or other Infor) name.
Here getReverseGeocode method call by [self getReverseGeocode];
- (void) getReverseGeocode
{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
if(currentLatLong.count > 0)
{
CLLocationCoordinate2D myCoOrdinate;
myCoOrdinate.latitude = LatValue;
myCoOrdinate.longitude = LangValue;
CLLocation *location = [[CLLocation alloc] initWithLatitude:myCoOrdinate.latitude longitude:myCoOrdinate.longitude];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error)
{
if (error)
{
NSLog(#"failed with error: %#", error);
return;
}
if(placemarks.count > 0)
{
NSString *MyAddress = #"";
NSString *city = #"";
if([placemark.addressDictionary objectForKey:#"FormattedAddressLines"] != NULL)
MyAddress = [[placemark.addressDictionary objectForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
else
MyAddress = #"Address Not founded";
if([placemark.addressDictionary objectForKey:#"SubAdministrativeArea"] != NULL)
city = [placemark.addressDictionary objectForKey:#"SubAdministrativeArea"];
else if([placemark.addressDictionary objectForKey:#"City"] != NULL)
city = [placemark.addressDictionary objectForKey:#"City"];
else if([placemark.addressDictionary objectForKey:#"Country"] != NULL)
city = [placemark.addressDictionary objectForKey:#"Country"];
else
city = #"City Not founded";
NSLog(#"%#",city);
NSLog(#"%#", MyAddress);
}
}];
}
}

Related

google places api - json return correct data and then an exception occurs

I'm using google places API,
The json return valid results, In my async methods I'm setting markers per each result, the method ends and all of a sudden i get the following thread 1: EXC_BAD_ACCESS code:2 address:0x9243e8, in the output window i get the following: [71539:c07] -[__NSCFString CGImage]: unrecognized selector sent to instance 0xb19dd10
I have no clue how to debug this, or where to look for the problem, all my parameters look good.
thanks
I suspect what you are doing is as a commenter says, treating the returned JSON object as a type it is not. Specifically, from your error, you are treating a string like an image. Generally, JSON doesn't not contain actually images, so when you deserialize it, you will not get images. In the Google Places API, it does not send you images. Instead, it send you URLs to images. You will have to download the images using the URLs you get from the Places API.
Here is some code I use. This method gets the place list from the API based on a CLLocation and returns it as a much more easily digested NSArray of NSDictionary objects each containing the important met-data pertaining to a place:
-(NSArray*)getPlaceListForLocation:(CLLocation*)inLocation {
NSString* requestStr = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=%#&location=%f,%f&radius=100&sensor=true",self.googleAPIKey,inLocation.coordinate.latitude,inLocation.coordinate.longitude];
NSURL* requestURL = [NSURL URLWithString:requestStr];
NSLog(#"Fetching place data with URL = %#", requestURL);
NSData* requestData = [NSData dataWithContentsOfURL:requestURL];
if ( nil != requestData ) {
NSError* jsonError = nil;
id resultsObj = [NSJSONSerialization JSONObjectWithData:requestData options:0 error:&jsonError];
if ( nil == resultsObj ) {
if ( nil != jsonError ) {
NSLog(#"%#",jsonError.description);
}
return nil;
}
NSArray* itemList = [resultsObj objectForKey:#"results"];
if ( nil == itemList || itemList.count == 0) {
return nil;
}
NSMutableArray* placeList = [NSMutableArray arrayWithCapacity:itemList.count];
for ( NSDictionary* item in itemList ) {
NSString* name = [item objectForKey:#"name"];
NSString* iconURL = [item objectForKey:#"icon"];
NSString* vicinity = [item objectForKey:#"vicinity"];
NSDictionary* geometryDict = [item objectForKey:#"geometry"];
NSDictionary* locationDict = [geometryDict objectForKey:#"location"];
NSMutableDictionary* itemDict = [NSMutableDictionary dictionaryWithCapacity:2];
if ( nil != name ) {
[itemDict setObject:name forKey:#"name"];
}
if (nil != iconURL ) {
[itemDict setObject:iconURL forKey:#"iconurl"];
}
if ( nil != vicinity ) {
[itemDict setObject:vicinity forKey:#"vicinity"];
}
if ( nil != locationDict ) {
NSString* latitudeStr = [locationDict objectForKey:#"lat"];
NSString* longitudeStr = [locationDict objectForKey:#"lng"];
double latitude = [latitudeStr doubleValue];
double longitude = [longitudeStr doubleValue];
CLLocation* placeLoc = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
CLLocationDistance distance = [inLocation distanceFromLocation:placeLoc];
[itemDict setObject:[NSNumber numberWithDouble:distance] forKey:#"distance"];
}
[placeList addObject:itemDict];
}
return placeList;
}
return nil;
}
Then I display these in a table view using the returned NSArray as the data source. In order to display the icon, I use something like MKNetworkImageView in order to fetch the icon based on the URL.

ios: What is the efficient and recommended way to reverse geocode?

I am to reverse Geo Code Latitude and Longitude of some location and want to get address of that location. I have done it through google web service but it takes time.
I want to know if there is some other good and efficient approach.
Currently calling this service,
NSString * getAddress = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/geocode/json?latlng=%#,%#&sensor=true",Lattitude,Longitude];
You can use CLGeocoder:
[self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error){
CLPlacemark *placemark = placemarks[0];
NSLog(#"Found %#", placemark.name);
}];
This will still take time though, since both methods use web services to convert lat / long into a place
Try This code .
geoCoder = [[CLGeocoder alloc]init];
[self.geoCoder reverseGeocodeLocation: locationManager.location completionHandler:
//Getting Human readable Address from Lat long,,,
^(NSArray *placemarks, NSError *error) {
//Get nearby address
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
NSString *locatedAt = [[placemark.addressDictionary valueForKey:#"FormattedAddressLines"] componentsJoinedByString:#", "];
//Print the location to console
NSLog(#"I am currently at %#",locatedAt);
}];
Have a look at GLGeocoder. Specifically reverseGeocodeLocation:completionHandler:
You can use Apple's CLGeocoder (part of CoreLocation). Specifically, the – reverseGeocodeLocation:completionHandler: method which will return a dictionary of address data for the given coordinates.
Have a look at this tutorial, or, if just want something to copy quickly: NSArray *addressOutput;
CLLocation *currentLocation;
//assumes these instance variables
// Reverse Geocoding
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray *placemarks, NSError *error) {
NSLog(#"Found placemarks: %#, error: %#", placemarks, error);
if (error == nil && [placemarks count] > 0) {
NSMutableArray *tempArray = [[NSMutableArray alloc] initWithCapacity:[placemarks count]];
for (CLPlacemark *placemark in placemarks) {
[tempArray addObject:[NSString stringWithFormat:#"%# %#\n%# %#\n%#\n%#",
placemark.subThoroughfare, placemark.thoroughfare,
placemark.postalCode, placemark.locality,
placemark.administrativeArea,
placemark.country]];
}
addressOutput = [tempArray copy];
}
else {
addressOutput = nil;
NSLog(#"%#", error.debugDescription);
}
}];
Based off the code in the tutorial.
If you to not want to use google API, try this code - basically transforms latitude and longitude inputs into ZIPs (can be adjusted to adresses).
pip install uszipcode
# Import packages
from uszipcode import SearchEngine
search = SearchEngine(simple_zipcode=True)
from uszipcode import Zipcode
import numpy as np
#define zipcode search function
def get_zipcode(lat, lon):
result = search.by_coordinates(lat = lat, lng = lon, returns = 1)
return result[0].zipcode
#load columns from dataframe
lat = df_shooting['Latitude']
lon = df_shooting['Longitude']
#define latitude/longitude for function
df = pd.DataFrame({'lat':lat, 'lon':lon})
#add new column with generated zip-code
df['zipcode'] = df.apply(lambda x: get_zipcode(x.lat,x.lon), axis=1)
#print result
print(df)
#(optional) save as csv
#df.to_csv(r'zip_codes.csv')

Convert NSString Latiude/Longitude Coordinates to City, State, and Timezone with CLGeocoder

I have a view controller that pulls the users latitude and longitude coordinates from the app delegate. This works well, but I also need the user's city, state, and time zone. I know I should use CLGeocoder for this (please see last chunk of code), but don't know how to put it together. I'd just need NSStrings of the city, state, and timezone. Anyone have any pointers or an example? Thank you!
In my App Delegate, I use CCLocationManager to get the Coordinates like this:
- (NSString *)getUserCoordinates
{
NSString *userCoordinates = [NSString stringWithFormat:#"latitude: %f longitude: %f",
locationManager.location.coordinate.latitude,
locationManager.location.coordinate.longitude];
locationManager = [[CLLocationManager alloc] init];
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[locationManager startUpdatingLocation];
return userCoordinates;
}
- (NSString *)getUserLatitude
{
NSString *userLatitude = [NSString stringWithFormat:#"%f",
locationManager.location.coordinate.latitude];
return userLatitude;
}
- (NSString *)getUserLongitude
{
NSString *userLongitude = [NSString stringWithFormat:#"%f",
locationManager.location.coordinate.longitude];
return userLongitude;
}
In my View Controller, I get the user's Latitude and Longitude as an NSString with this:
NSString *userLatitude =[(PDCAppDelegate *)[UIApplication sharedApplication].delegate
getUserLatitude];
NSString *userLongitude =[(PDCAppDelegate *)[UIApplication sharedApplication].delegate
getUserLongitude];
I would like to get the city, state, and timezone. I understand I need CLGeocoder, but can't figure out how to meld it together:
CLGeocoder * geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks,
NSError *error) {
for (CLPlacemark * placemark in placemarks) {
NSString *locality = [placemark locality];
}
}
A couple things, Brandon:
1) CLLocationManager might not give you an instant response to your request for coordinates. You should set your view controller as a CLLocationManager delegate and then when the location update comes in (which will be in the locationManager:didUpdateLocations: method), then you can run your CLGeocoder method.
2)
Which I wrote to look like this:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog( #"didUpdateLocation!");
NSLog( #"latitude is %# and longitude is %#", [self getUserLatitude], [self getUserLongitude]);
CLGeocoder * geoCoder = [[CLGeocoder alloc] init];
[geoCoder reverseGeocodeLocation:locationManager.location completionHandler:^(NSArray *placemarks, NSError *error) {
for (CLPlacemark * placemark in placemarks) {
NSString * addressName = [placemark name];
NSString * city = [placemark locality]; // locality means "city"
NSString * administrativeArea = [placemark administrativeArea]; // which is "state" in the U.S.A.
NSLog( #"name is %# and locality is %# and administrative area is %#", addressName, city, administrativeArea );
}
}];
}
Getting the location's timezone is a bit trickier. I bet there's an API or some sample code to get it within iOS, but it's not a part of the CLPlacemark API.
Form a CLLocation from latitude and longitude double value. Then feed that location to reverseGeocodeLocation:completionHandler:
Also note that the method reverseGeocodeLocation:completionHandler: is asynchronous.
You can also use, CLLocationManagerDelegate's locationManager:didUpdateHeading:
to asynchronously update if there is an Location available, which is better.
Anyway following your approach, just modifying some of your code from AppDelegate
- (double)getUserLatitude
{
return retrun locationManager.location.coordinate.latitude;
}
- (double)getUserLongitude
{
retrun locationManager.location.coordinate.longitude;
}
-(CLLocationManager*) getLocationManager
{
return locationManager;
}
Now Form a Location object
double latt = [(PDCAppDelegate *)[UIApplication sharedApplication].delegate getUserLatitude];
double longt = [(PDCAppDelegate *)[UIApplication sharedApplication].delegate getUserLongitude];
CLLocation loc = [[CLLocation alloc] initWithLatitude:latt longitude:longt]
or you can directly get the location object from CLLocationManager
CLLocation loc = [(PDCAppDelegate *)[UIApplication sharedApplication].delegate getLocationManager].location;
Then you can use your code feeding the location to reverseGeocodeLocation:completionHandler: and Get the CLPlaceMark
[geoCoder reverseGeocodeLocation:loc completionHandler:^(NSArray *placemarks,
NSError *error) {
for (CLPlacemark * placemark in placemarks) {
NSString *locality = [placemark locality];
NSString * name = [placemark name];
NSString *country = [placemark country];
/*you can put these values in some member vairables*/
m_locality = [placemark locality];
m_name = [placemark name];
m_country = [placemark country];
}
}
While I don't have a solution for the timezone issue (I agree with others who've answered this question - look for a non-Apple API), I thought I'd provide an answer in Swift, for those who are curious:
func provideGeocodedStringForLocation(location: CLLocation, withCompletionHandler completionHandler: (String) -> ()) {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { (placemarks: [CLPlacemark]?, error: NSError?) -> Void in
guard let placemarks = placemarks where placemarks.count > 0 && error == nil else {
if let error = error { print(error.localizedDescription) }
completionHandler("Earth")
return
}
let city = placemarks[0].locality ?? ""
let state: String
if let adminArea = placemarks[0].administrativeArea {
state = ", \(adminArea)"
} else {
state = ""
}
completionHandler("\(city)\(state)") // produces output similar to "Boulder, CO"
}
}
// get the lat/long from your UIAppDelegate subclass
let latitude = CLLocationDegrees("40.0176")
let longitude = CLLocationDegrees("-105.28212")
let location = CLLocation(latitude: latitude, longitude: longitude)
provideGeocodedStringForLocation(location) { print($0) }
I got timezone name from placemark description. And it works for ios 8 and higher.
You can check this link for how to get time zone

Core Data optimization

I have a large list of Users in an NSDictionary that has a structure like this:
97 = {
birthday = "";
gender = Unspecified;
image = {
status = Prepared;
type = Image;
};
"name_display" = "Facebook User";
"name_first" = Facebook;
"name_last" = User;
type = Contact;
"user_id" = 97;
};
98 = {
birthday = "";
gender = Unspecified;
image = {
status = Prepared;
type = Image;
};
"name_display" = "Facebook User";
"name_first" = Facebook;
"name_last" = User;
type = Contact;
"user_id" = 98
}
I want to input this data into Core Data. First I must check if the user already exists in core data. If so, update that user. Otherwise create a new user. The way I am doing it works, but it is extremely slow. Here's my code:
NSDictionary *users = [responseData objectForKey:#"users"];
if (users) {
for (id userKey in [users allKeys]) {
NSDictionary *contactDictionary = [users objectForKey:userKey];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userID == %#", userKey];
NSUInteger count = [CoreDataHelper countForEntity:#"Contact" withPredicate:predicate andContext:[FSAppDelegate managedObjectContext]];
if (count > 0) {
NSMutableArray *existingContactArray = [CoreDataHelper searchObjectsForEntity:#"Contact" withPredicate:predicate andSortKey:nil andSortAscending:NO andContext:[FSAppDelegate managedObjectContext]];
Contact *existingContact = [existingContactArray objectAtIndex:0];
[CoreDataHelper updateContactWithDictionary:contactDictionary forContactObject:existingContact];
}
else {
Contact *newContact = (Contact*)[NSEntityDescription insertNewObjectForEntityForName:#"Contact" inManagedObjectContext:[FSAppDelegate managedObjectContext]];
[CoreDataHelper updateContactWithDictionary:contactDictionary forContactObject:newContact];
}
}
NSError *error;
if (![[FSAppDelegate managedObjectContext] save:&error]) {
// Handle the error.
NSLog(#"error saving to db - fsrequest class.");
}
}
And here is my method to update the contact
+(BOOL)updateContactWithDictionary:(NSDictionary*)changedContact forContactObject:(Contact*)contact {
NSString *bday = [changedContact valueForKey:#"birthday"];
NSString *gender = [changedContact valueForKey:#"gender"];
NSString *nameDisplay = [changedContact valueForKey:#"name_display"];
NSString *nameFirst = [changedContact valueForKey:#"name_first"];
NSString *nameLast = [changedContact valueForKey:#"name_last"];
NSString *type = [changedContact valueForKey:#"type"];
NSString *userID = [NSString stringWithFormat:#"%#",[changedContact valueForKey:#"user_id"]];
NSString *imageStatus = [[changedContact objectForKey:#"image"]objectForKey:#"status"];
NSString *imageType = [[changedContact objectForKey:#"image"]objectForKey:#"type"];
NSString *imageURL = [[changedContact objectForKey:#"image"]objectForKey:#"url"];
NSString *imageThumb = [[changedContact objectForKey:#"image"]objectForKey:#"url_thumb"];
NSString *locationName = [[changedContact objectForKey:#"location"]objectForKey:#"name"];
[contact setBirthday:bday];
[contact setGender:gender];
[contact setNameDisplay:nameDisplay];
[contact setNameFirst:nameFirst];
[contact setNameLast:nameLast];
[contact setType:type];
[contact setUserID:userID];
[contact setImageStatus:imageStatus];
[contact setImageType:imageType];
if (imageURL && !((NSNull *)imageURL == [NSNull null])) {
[contact setImageURL:imageURL];
}
if (imageThumb && !((NSNull *)imageThumb == [NSNull null])) {
[contact setImageThumbURL:imageThumb];
}
if (locationName && !((NSNull *)locationName == [NSNull null])) {
[contact setLocationName:locationName];
}
return YES;
}
Can someone give me an example of how I would do this in a much faster way? Some people have mentioned some ideas, but I need to see it to understand. Thanks!
First of all I'd move save: outside the loop. Replace:
// save core data
NSError *error;
if (![[FSAppDelegate managedObjectContext] save:&error]) {
// Handle the error.
NSLog(#"error saving to db - fsrequest class.");
}
}
}
with
}
// save core data
NSError *error;
if (![[FSAppDelegate managedObjectContext] save:&error]) {
// Handle the error.
NSLog(#"error saving to db - fsrequest class.");
}
}
Also, do you have some default values for imageURL, imageThumb and locationName defined in Core Data model? If no, why do you check for nulls (twice)?
Bonus:
It may be a good idea to eliminate countForEntity:withPredicate:andContext: call, like this:
NSMutableArray *existingContactArray = [CoreDataHelper searchObjectsForEntity:#"Contact" withPredicate:predicate andSortKey:nil andSortAscending:NO andContext:[FSAppDelegate managedObjectContext]];
if ([existingContactArray count] > 0)
{
Contact *existingContact = [existingContactArray objectAtIndex:0];
[CoreDataHelper updateContactWithDictionary:contactDictionary forContactObject:existingContact];
}
You need to understand that a fetch request is expensive (it needs to run SQL and do I/O). I'm not sure where your CoreDataHelper comes from, but it will do some sort of NSFetchRequest which is expensive. You can construct a single NSFetchRequest which will tell you which objects already exists. That will reduce the cost from O(N) to O(1).
[request setPredicate:[NSPredicate predicateWithFormat:#"userID IN %#", allKeys]];
And, as noted above, move the saves out of the loop. But if you're updating adding a many objects, you might want to save every now and then.

reverse geocoding and localization

I use this code to set a label with a location string
locationString = [[NSString alloc] initWithFormat:#"%#%# - %# %#%#",
thoroughfare,subThoroughfare,postalCode,
locality,countryCode];
locationLabel.text = locationString;
where thoroughfare, subThoroughfare, postalCode, locality,countryCode are obtained from a placemark.
Now, I'd like to visualize this string according the current locale. Have I specify a string format for each locale in which I'm interested or there is a simpler way to obtain this?
Thanks,
Fran
you can use following function
-(void) setLocation:(NSString *)latitude withLongitude:(NSString *)longitude {
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLLocation *location = [[CLLocation alloc] initWithLatitude:[latitude doubleValue] longitude:
longitude doubleValue]];
CLGeocodeCompletionHandler completionHandler = ^ (NSArray *placemarks, NSError *error){
if (error){
NSLog(#"error in fetching location <%#>",error);
return ;
}
if ( placemarks && placemarks.count >0){
CLPlacemark *mark = [placemarks objectAtIndex:0];
NSString *addresstring = [[mark addressDictionary] objectForKey:#"FormattedAddressLines"] componentsJoinedByString:#","];
*//fetched addressDictionary for key FormattedAddressLines*
}
The addressDictionary property of the placemark object should resolve in part the problem with its FormattedAddressLines array.