Drupal - Build a Mobile App to Geo Locate Node Content

Category:

In this tutorial we will explore how to build a mobile application for Android (iOS, iPhone, iPad) using PhoneGap and jQueryMobile that can be used to geo locate node content from a Drupal website. The mobile application will lookup the user's current location, then ask Drupal for a list of content closest to that user's location.

Tutorial Components

  • Drupal 7.x
  • PhoneGap 2.x
  • jQueryMobile 1.2

Before getting started, it is highly recommended you are familiar with the 'Hello World' for PhoneGap.

Tutorial Prerequisites

  • PhoneGap Getting Started Guide

Ready? Go!

1. Download and Install Drupal

2. Download and Enable Required Drupal Modules (chances are your site already has some of these modules installed)

Here is a list of modules that are needed by your Drupal site. I've included the recommended versions of the modules (at the time of writing this) and the names of the modules to enable.

After you've downloaded and enabled all these modules, let's flush Drupal's cache, twice (just to be safe, safety first)!

3. Create a Drupal Content Type with a Location Field

We will need a content type in Drupal that has a Location Field on it. In this example we'll create a content type called 'Building' (with a machine name of: building) then we will add a Location Field to it called Location (with a machine name of: field_building_location).

  • Add Content Type (admin/structure/types/add)
    • Use default settings
  • Add Location Field to New Content Type (admin/structure/types/manage/building/fields)
    • Change the Collection Settings to 'Allow' on: Location name, Street location, Additional, City, State/Province, Postal code, Country, Coordinate Chooser
    • Required field: Not checked
    • Number of values: 1

4. Add Some Content with Locations

Drupal Location Form FieldsNow that we have a content type setup with a location field, let's create some nodes with locations. Go to node/add and click on your new content type. Now fill out the node creation form fields:

  • Node Title
  • Location (screen shot)
    • Location name
    • Street
    • Additional
    • City
    • State/Province
    • Postal code
    • Country
    • Latitude (optional)
    • Longitude (optional)

I would recommend adding at least 5 different nodes with real locations nearby your actual location. That way when we use geo-location later, we can find content near you.

Note, you can acquire latitude and longitude data for a location with Google Maps. Just right click on the desired location on the map, then click "What's here?" and that will place the lat/lng coordinates in the Map's text field.

5. Create a Drupal View to Retrieve Geo Located Content 

Let's now create a new view to handle locating our Building nodes. Go to admin/structure/views/add and create a new view with the following settings:

  • View name: Geo Located Buildings
  • Machine name: geo_located_buildings
  • Show Content of type Building sorted by Unsorted
  • Create a page: Checked
    • Page title: Geo Located Buildings
    • Path: geo-located-buildings
    • Display format: JSON data document (of fields)
    • Items to display: 0 (zero means give me all records)
    • Use a pager: Not checked
    • Create a menu link: Not checked
    • Include an RSS feed: Not checked

Here's a screen shot example of creating a new view:

Click on 'Continue & edit' when you're done.

5a. Observe JSON output in View preview

Once you continue onto your view's edit screen, scroll down to the bottom and verify that your view is producing some JSON output similar to this:

{
  "nodes" : [
    {
      "node" : {
        "title" : "Casa Bonita"
      }
    },
    {
      "node" : {
        "title" : "Harlan Hatcher Graduate Library"
      }
    },
    {
      "node" : {
        "title" : "Art, Architecture and Engineering Library"
      }
    },
    {
      "node" : {
        "title" : "Michigan Stadium"
      }
    },
    {
      "node" : {
        "title" : "Nichols Arboretum"
      }
    },
    {
      "node" : {
        "title" : "North Quad"
      }
    }
  ]
}

5b. Add a Location Field onto the View

When we ask Drupal for the nodes closest to us, we'll want to know how far away the nodes are from us, so we'll add a "Location: Distance / Proximity" field to our view.

When configuring the field, we'll use these options:

  • Units: Miles
  • Origin: Latitude / Longitude from views argument
  • Create a label: Checked
    • Label: distance

Feel free to adjust the 'Units' value to kilometers.

5c. Add a Contextual Filter for a Location onto the View

Next up we need to add a contextual filter to our view. This will allow us to send our coordinates to the view so it can return to us the Buildings that are closest to us. For example, add this contextual filter for All Displays:

  • Location: Distance / Proximity

On the next screen, configure the "Location: Distance / Proximity" contextual filter with these settings, for example:

  • When the filter value is not in the URL: Display contents of "No results found"
  • Coordinate Type: Decimal Latitude and Longitude coordinates, comma delimited
  • Distance unit: Miles
  • Method: Rectangular Proximity

The other options on this page are optional and aren't really needed for our task at hand.

5d. Test the Location Contextual Filter with Our Location

To test out our new contextual filter, we will need our current coordinates. To retrieve your coordinates (latitude and longitude)

  1. Go to Google Maps
  2. Find your current position
  3. Right click on your current position
  4. Select "What's here?"
  5. Copy the latitude and longitude coordinates that appear in the Google Maps text field search box

For this example, our current location will be at Bandemer Park in Ann Arbor, Michigan. The approximate latitude and longitude is:

  • 42.299691
  • -83.743207

Now that we have our coordinates, we can pass them in as a contextual filter to our view so we can retrieve the nodes that are closes to us. When passing in our coordinates we also need to give a distance range of how far we would like the result set to span. So, let's say for example we wanted a list of nodes within 10 miles of our current position. We would then pass this as our contextual argument to the view:

  • 42.299691,-83.743207_10

The "_10" specifies the 10 mile range. We can now take this input and place it into the "Preview with contextual filters" text field at the bottom of our view:

Now if we click the "Update preview" button, we should get a JSON list of nodes within 10 miles of our current location:

{
  "nodes" : [
    {
      "node" : {
        "title" : "Harlan Hatcher Graduate Library",
        "distance" : "1.61 mi"
      }
    },
    {
      "node" : {
        "title" : "Art, Architecture and Engineering Library",
        "distance" : "1.50 mi"
      }
    },
    {
      "node" : {
        "title" : "Michigan Stadium",
        "distance" : "2.35 mi"
      }
    },
    {
      "node" : {
        "title" : "Nichols Arboretum",
        "distance" : "1.75 mi"
      }
    },
    {
      "node" : {
        "title" : "North Quad",
        "distance" : "1.33 mi"
      }
    }
  ]
}

5e. Add Sort Criteria onto the View to Retrieve Nodes Closest to Us

When Drupal retrieves the nodes closest to us, we would generally want them returned in the order closest to us. So add a 'Sort Criteria' for "Location: Distance / Proximity" onto your view.

When configuring the sort criteria, use the following settings:

  • Origin: Use Distance / Proximity filter
  • Sort ascending

Warning, there appears to be a bug with this sort option in D7's Location module (7.x-3.0-alpha1), in that it won't actually sort by distance.  Checkout a way to get around this bug.

5f. Disable 'Views API mode' for the JSON data document Format settings

Click on 'Settings' next to 'JSON data document' under the view 'Format' section. There you need to un-checked the 'Views API mode' checkbox. If you don't do this, your JSON output will be rendered into a Drupal HTML page instead of being output as only JSON.

5g. Save the View and Test it Out

OK, you can finally click 'Save' on your view and test it out.

After saving your view, open the following example URL in your browser:

  • http://www.example.com/?q=geo-located-buildings/42.299691,-83.743207_10

That should give you a JSON result set of the nodes within a 10 mile range of the given coordinates. The output should look similar to the JSON output displayed previously.

6. Setup Eclipse, Android Emulator and PhoneGap to Build Our Mobile Application

If you haven't already done so, you'll need to download and install Eclipse, setup the Android Emulator in Eclipse and have PhoneGap setup to run on your Android Emulator. Please refer to these tutorials for more information:

7. Download jQuery & jQueryMobile

Download jQuery & jQueryMobile. Place the JQuery Javascript file and both the Javascript and CSS files for JQuery Mobile inside your /assets/www/ directory in your Android Project.

  • /assets/www/jquery-1.8.3.min.js
  • /assets/www/jquery.mobile-1.2.0.min.js
  • /assets/www/jquery.mobile-1.2.0.min.css

8. Create Custom Javascript File

We will need a custom javascript file to handle mobile application events and for communicating with Drupal. Create a javascript file called drupal_geo_locate.js inside the assets/www directory of your PhoneGap mobile application.

  • assets/www/drupal_geo_locate.js

9. Include JQuery & JQuery Mobile in the Mobile Application

Inside your Eclipse PhoneGap Android project's /assets/www/index.html file, include the .js files and the .css file like so:

<!DOCTYPE HTML>
<html>
  <head>
    <title>Drupal Geo Locate Nodes</title>
    <script type="text/javascript" charset="utf-8" src="cordova-2.1.0.js"></script>
    <link rel="stylesheet" href="jquery.mobile-1.2.0.min.css" /> 
    <script src="jquery-1.8.3.min.js"></script>
    <script src="jquery.mobile-1.2.0.min.js"></script>
  </head>
  <body>
    <h1>Hello World</h1>
  </body>
</html>

When we run this in an Android emulator, it will look something like this:

Best looking mobile application ever, right? OK, let's spice it up a bit with some jQueryMobile.

10. Create a jQueryMobile Dashboard Page with One Button

Next up, let's use jQueryMobile to add a page to our mobile application. We will call this page the Dashboard and add one button to it. When that button is clicked, we will handle the click event by retrieving our current geo location using PhoneGap.

Replace the <body></body> of the index.html file with this:

<body>

  <div data-role="page" id="dashboard">

    <script type="text/javascript" charset="utf-8" src="drupal_geo_locate.js"></script>

    <div data-role="header"><h1>Dashboard</h1></div><!-- /header -->

    <div data-role="content">

      <a href="#" id="geo_locate_button" data-role="button">Geo Locate Buildings</a>

      <p id="geo_locate_message"></p>

    </div><!-- /content -->

  </div><!-- /page -->

</body>

From the example code above, we create a jQueryMobile page with an id of "dashboard", included our custom drupal_geo_locate.js file, added a button to initiate the geo location, and an empty paragraph to store result messages. Now when we run our mobile app, it will look something like this:

11. Perform Geo Location Call to Drupal When Button is Clicked

In our custom javascript file drupal_geo_locate.js, we'll add some code to handle the click on the button. When the click happens, we'll use PhoneGap to determine our current geo location. Add this code to your drupal_geo_locate.js file:

/**
 * Button Click Handler
 */
$('#geo_locate_button').live('click', function(){
  $('#geo_locate_message').html('Getting current position...');
  navigator.geolocation.getCurrentPosition(onSuccess, onError, {enableHighAccuracy: true});
  return false;
});

/**
 * Geo Location onSuccess
 */
function onSuccess(position) {
  var message = 'Latitude: '  + position.coords.latitude + '<br />' +
                'Longitude: ' + position.coords.longitude;
  $('#geo_locate_message').html(message);
}

/**
 * Geo Location onError
 */
function onError(error) {
  alert('code: '    + error.code    + '\n' +
        'message: ' + error.message + '\n');
}

Now when we click our button, the mobile app will try to determine our current location. However, since we are in an emulator, after clicking the button, the mobile app will just wait and wait trying to retrieve our geo location. Instead, we need to take an extra step so our geo location can be determined, see step 12. If you are compiling the application straight to a mobile device, then you can skip the next step because your device does actually have a geo location, as opposed to an emulator which just has a simulated location.

12. Set Android Emulator Geo Location Simulation (required only for Android Emulator development)

If you are following along with this tutorial and using an Android Emulator, you may be wondering what the Geo Coordinates of your emulator are. Well, that is up to you. You need to simulate them using DDMS in Eclipse:

  1. Window -> Open Perspective -> DDMS
  2. In the 'Quick Access' search bar in DDMS, search for 'Emulator Control' and click on it when it appears
  3. Scroll down to the 'Location Controls' section on the 'Emulator Control' tab
  4. Enter your 'Manual' 'Decimal' latitude and longitude coordinates you acquired earlier
  5. Click the Send button

Please note the Longitude field comes first here. I banged my head on the desk for a while because I didn't notice I entered my coordinates backwards.

13. Observe Our Geo Location

Now when we ran our app, click the 'Geo Locate Buildings' button and got a response, we should see something like this:

14. Send Drupal Our Geo Location Coordinates to Retrieve Closest Buildings

Now that our onSuccess function is getting called when our geo location coordinates are retrieved via PhoneGap, let's make a call to our Drupal view and send along our latitude, langitude, and the range (in miles) we would like to search for nearby buildings.

/**
 * Geo Location onSuccess
 */

function onSuccess(position) {
  var message = 'Latitude: '  + position.coords.latitude + '<br />' +
                'Longitude: ' + position.coords.longitude;
  $('#geo_locate_message').html(message);
  drupal_geo_locate_buildings(position.coords.latitude, position.coords.longitude);
}

/**
 * Call Drupal View for Nearby Buildings
 */
function drupal_geo_locate_buildings(lat, lng) {
  try {
    
    var range = 10;
    
    var drupal_view_url = "http://10.0.2.2/mobile.lib.umich.edu/?q=geo-located-buildings/" + 
      lat + "," + 
      lng + "_" + 
      range;
      
    $.ajax({
      url: drupal_view_url,
      type: 'get',
      dataType: 'json',
      error: function (XMLHttpRequest, textStatus, errorThrown) {
        alert('drupal_geo_locate_buildings - failed to get buildings');
      },
      success: function (data) {
        alert('Success, we found ' + data.nodes.length + ' node(s)!');
      }
    });

  }
  catch (error) { alert("drupal_geo_locate_buildings - " + error); }
}

Note the use of 10.0.2.2 instead of localhost for Android emulators.

Now if we run our app, click the button (use DDMS to send our coordinates if we are in an emulator), it will grab our geo location, send it off to the Drupal view and tell us how many nodes it found.

15. Display Nearby Building Node Results in Mobile Application

Now that we are successfully retrieving nearby buildings, let's display them in a list on the app.

Modify your index.html to include an empty unordered list inside the dashboard page data-role="content" div:

<div data-role="content">
  <p><a href="#" id="geo_locate_button" data-role="button">Geo Locate Buildings</a></p>
  <p id="geo_locate_message"></p>
  <ul data-role="listview" id="building_list"></ul>
</div><!-- /content -->

Modify the drupal_geo_locate_buildings ajax success callback function to add each building node to our new unordered list:

success: function (data) {
      
  // Clear the list.
  $("#building_list").html("");

  // For each building node result, add it to the building list.
  $.each(data.nodes,function(index,obj){
    html = "(" + obj.node.distance + ") " + obj.node.title;
    $("#building_list").append($("<li></li>",{"html":html}));
  });

  // Refresh the list.
  $("#building_list").listview("destroy").listview();

}

Now, when we run the app and click the button, we should have a list of building nodes retrieved that looks something like this:

 

Conclusion

We can see a small taste of the cool things we can do with these tools. Drupal and PhoneGap with jQuery and jQueryMobile can be used to create all kinds of mobile applications. Good luck in your coding adventures!

Related Topic

Comments

thanks

You are very welcome, enjoy!

This is the best (well, really it's the only, but still it's well done and thorough) introduction to the Services module using the admin GUI that I've been able to find on the web.  Thanks for this.

Thank you Charlie!

Just finished implementing your tutorial - Mobile Application with Drupal 7 Services, PhoneGap & JQuery Mobile for Android.

Excellent stuff.. your advice #3 https://drupal.org/node/2013781 was timely.

 Thanks a lot.. Tyler!! 

Thanks Binu!

Really it is GREAT Information Thank You :)

very nice indeed tyler!