Using the Google Places API With MapKit

This is a post by iOS Tutorial Team Member Jason van Lint, the founder and owner of Dead Frog Studios, a boutique design and app building studio in Neuchatel, Switzerland. In the past, we wrote a tutorial on MapKit showing you how you can display your current location on the map and plot some information […] By .

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Take Your Places!

To retrieve Google Places data, you send a query to a specific Google URL, and get a JSON response from Google that you can parse for the relevant info. The URL request takes this form:

https://maps.googleapis.com/maps/api/place/search/output?parameters

The output can be either JSON or xml. For this app, you’ll use JSON.

Certain parameters are required to initiate a Place Search request. As is standard in URLs, all parameters are separated using the ampersand (&) character. Here is a list of the mandatory parameters:

  • key — Your application’s API key. This key identifies your application for purposes of quota management, and to ensure that Places added from your application are made immediately available to your app. Visit the API Console to create an API Project and obtain your Google Places API key.
  • location — The latitude/longitude for which you want to retrieve Place information. This must be specified as latitude,longitude.
  • radius — Defines the distance (in meters) within which to return Place results. The maximum allowed radius is 50,000 m.
  • sensor — Indicates whether or not the Place request came from a device using a location sensor (e.g., a GPS) to determine the location sent in this request. This value must be either true or false.

There’s an additional optional parameter of interest to you:

  • types — Restricts the results to Places matching at least one of the specified types. Types should be separated with a pipe symbol (type1|type2|etc.).

An example request might look like this:

https://maps.googleapis.com/maps/api/place/search/json?location=-33.8670522,151.1957362&radius=500&types=food&sensor=true&key=AddYourOwnKeyHere

You’re going to have to build this request string using a location in latitude and longitude form, and include the “type” of establishment you’re looking for. Your app will just look for bars, cafes, florists, ATMs and parks, but there are a whole host of keywords that Google accepts as parameters for “type.” The full list can be seen here.

With regard to the “key” parameter mentioned above, the Google Places API uses an API key to identify your application. API keys are managed through the Google API Console. To activate the Places API and create your key:

  1. Visit the API Console and log in with your Google Account.
  2. A default project called API Project is created for you when you first log in to the console. You can use the project or create a new one by clicking the API Project button at the top of the window and selecting Create.
  3. Click the Services link from the left-hand menu.
  4. Click the Status switch next to the Places API entry. The switch slides to On.
  5. Click API access from the left navigation. Your key is listed in the Simple API Access section.

Once you have your key, it’s a good idea to define it as a constant in your header file.

Add this line to ViewController.h below the #import code. Make sure you replace the “API Google Key Here” text with the key you obtained from the steps above.

#define kGOOGLE_API_KEY @"API Google Key here"

In the screenshot below, you can see how I’ve defined my API key as a constant.

So, you have your key and a basic understanding of what you want to send to Google for place data. Here are the remaining steps that will tie the pieces of your app together:

  1. You need to build a search string based on where you are on the map and the zoom level. This may have changed from your start location if the user has moved around on the map.
  2. You need the toolbar buttons to initiate a search and to indicate in your URL string which type of establishment you want to look for.
  3. You need to send this request to Google and parse the JSON response received.
  4. You need to plot these locations as pins on the map.

Get Set

It may be #2 in the list above, but hooking up your toolbar buttons is the best place to start. After all, you need them to help build your Google query URL.

Select MainStoryboard.storyboard and click on the Assistant Editor icon again (if the Assistant editor isn’t open). If you have ViewController.h open in the code window, use the toolbar above the code window to select ViewController.m instead:

Select the Bar toolbar button and Ctrl-drag a line from the toolbar button to your code window, just above the #pragma mark indicating mapView delegates. A popup will appear. In the name field type “toolBarButtonPress” and click OK.

This will create a new method and connect it to the toolbar button. Now select the “Cafe” button and Ctrl-drag a line from the button to the top line of the method you just created. A blue box should highlight the entire method, informing you that you’re about to connect this button to the existing method in your code.

Repeat the above step for all the buttons in the toolbar. Ctrl-drag a line from each button to same method you created with the very first Ctrl-drag.

With all the buttons connected to the same method, how will you identify which one has been pressed and what value it represents? For that, you’re going to use a little code to get the title of the button.

Add this code to your newly-created method:

    UIBarButtonItem *button = (UIBarButtonItem *)sender; 
    NSString *buttonTitle = [button.title lowercaseString];

The sender value for the method is always the button that was pressed to initiate the action. So in the above code, you convert the sender into a UIBarButtonItem to retrieve the button title.

You then convert it to lowercase, as the Google URL request will only accept lowercase place types. The string buttonTitle is the first component of your query string for Google!

Ready Query

This is a good time to start thinking about how you’re going to build the string to send to the Google API. In ViewController.m, insert the following code:

-(void) queryGooglePlaces: (NSString *) googleType {
    // Build the url string to send to Google. NOTE: The kGOOGLE_API_KEY is a constant that should contain your own API key that you obtain from Google. See this link for more info:
    // https://developers.google.com/maps/documentation/places/#Authentication
    NSString *url = [NSString stringWithFormat:@"https://maps.googleapis.com/maps/api/place/search/json?location=%f,%f&radius=%@&types=%@&sensor=true&key=%@", currentCentre.latitude, currentCentre.longitude, [NSString stringWithFormat:@"%i", currenDist], googleType, kGOOGLE_API_KEY];
    
    //Formulate the string as a URL object.
    NSURL *googleRequestURL=[NSURL URLWithString:url];
    
    // Retrieve the results of the URL.
    dispatch_async(kBgQueue, ^{
        NSData* data = [NSData dataWithContentsOfURL: googleRequestURL];
        [self performSelectorOnMainThread:@selector(fetchedData:) withObject:data waitUntilDone:YES];
    });
}

This method receives a “type” of place and uses it to build the string. The “url” variable stores your created query to the Google API, and converts the NSString into a NSURL. Finally, you use the asynchronous data-fetching technique that you can read more about in the JSON tutorial available on this site.

The retrieving code has a method called fetchedData that needs to be defined, so go ahead and create that method:

-(void)fetchedData:(NSData *)responseData {
    //parse out the json data
    NSError* error;
    NSDictionary* json = [NSJSONSerialization 
                          JSONObjectWithData:responseData 
                          
                          options:kNilOptions 
                          error:&error];
    
    //The results from Google will be an array obtained from the NSDictionary object with the key "results".
    NSArray* places = [json objectForKey:@"results"]; 
    
    //Write out the data to the console.
    NSLog(@"Google Data: %@", places);
}

This method simply processes the results you receive from the Google API. You are interested in the results array of the returned JSON file, so you assign an array called “places” to read all values from the key called “results.” You use NSLog to print the results to the console. That will show you the data you get back from Google.

You’re not ready to build or run just yet. Notice that your queryGooglePlaces: method is complaining about a couple of undeclared or missing variables. And there’s a constant in your asynchronous results retriever that you haven’t defined.

Fix the constant first. Go to ViewController.h and add the following code below the #import section:

#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

If you want to learn more about the above line, please refer again to the iOS 5.0 JSON tutorial available here.

The second error is Xcode complaining about a missing “currentCentre” variable. You can see that you’re trying to construct a URL query using four elements:

  1. Your place “type”
  2. Your Google API key
  3. A place from which to begin your search. This will be a central point that serves as the center of the circle from which you will spread out to find places of the desired type.
  4. A distance out from this center point to which the search will be limited. You want the distance to match the current “zoom” level on the map the user has selected. Zoomed out would mean searching a greater area than being centered on, say, just one city block.

Your place type is provided as a parameter passed in to your method. Your Google API key is defined by the constant you added before. However, you haven’t yet defined how you are going to obtain from and send to Google your current position on the map. Moreover, you haven’t worked out how to determine the map zoom level.

To do this, you’ll use two instance variables in ViewController.h and a delegate method of MapKit to update your position and zoom level as the user moves the map around on the device.

First declare your variables. Go to ViewController.h and add the following variables below the existing declaration for locationManager:

    CLLocationCoordinate2D currentCentre;
    int currenDist;

The variable that will hold the coordinates of the center position on the map needs to be of the special type CLLocationCoordinate2D. Your zoom level, however, is just an integer and requires no special type.

Next, ensure that these two instance variables are updated as the user interacts with the map. Go to ViewController.m and add the following method:

-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
    //Get the east and west points on the map so you can calculate the distance (zoom level) of the current map view.
    MKMapRect mRect = self.mapView.visibleMapRect;
    MKMapPoint eastMapPoint = MKMapPointMake(MKMapRectGetMinX(mRect), MKMapRectGetMidY(mRect));
    MKMapPoint westMapPoint = MKMapPointMake(MKMapRectGetMaxX(mRect), MKMapRectGetMidY(mRect));
    
    //Set your current distance instance variable.
    currenDist = MKMetersBetweenMapPoints(eastMapPoint, westMapPoint);
    
    //Set your current center point on the map instance variable.
    currentCentre = self.mapView.centerCoordinate;
}

This delegate method will be called every time the user changes the map by zooming or by scrolling around to a new position.

To get the current zoom distance on the map, you need to do a little math. First you get the east and west points of the map, and then you make a call to the MKMetersBetweenMapPoints method that calculates the distance between these two points. You store the result in the currentDist instance variable.

Getting the center point is much easier. You simply get the centerCoordinate property of your map view and assign it to your instance variable.

This should have removed any warnings from your string building method, but before you build and run, you need to fix one more thing.

toolBarButtonPress: is collecting a place “type” for you, but you need to send this type to your URL-building method to generate the query.

Add this code to the end of toolbarButtonPress::

    //Use this title text to build the URL query and get the data from Google.
    [self queryGooglePlaces:buttonTitle];

This is how you’ll send your button press string to your query string-building method.

OK, now you can finally build and run your code!

Try pressing the buttons on your toolbar, and watch the results that appear in the console output window. You should see a long list of places matching the type of place you selected, along with latitude and longitude data required to plot these points on the map.

Places3

If you click on a place type that can’t be found close to the coordinates you passed, you will just receive a blank JSON response and there will be no data in the console output window.

Now that you know you are getting data, let’s start plotting these places on the map!