Tag Archives: Geo

Code the Road Android App: Bike with a map on your wrist using the Google Maps APIs



Editor’s Note: We are (literally) coding a mobile app on the road as part of our cross-country road trip, Code the Road. This is our fourth (and final) post in the series -- if you haven’t read our previous posts, be sure to check out Hitting the Road with the Google Maps Android API, Store Your Locations in the Cloud with Firebase, and Checking in using the Places API to get started. Follow along as we build our app on the road and read more from Dave Overstrom, Program Manager at Google.

It’s been 22 days since you last saw us jogging on ICON’s NordicTrack treadmills back in Logan, UT, and to be honest, sitting in a 1959 GM Tour Bus for that long on the road has not exactly been ideal for our legs and joints. Luckily, we have just the solution to get us back in shape—thanks to the Google Maps APIs and Android Wear.

In this post, we are going to escape the Code the Road bus for a little while and make use of the Directions API, Elevation API, and Google Maps API for Android Wear to plan the perfect bike ride. We will build upon our application to:

  1. Find the perfect bike route using the Google Directions API
  2. Determine the elevation profile of the route leveraging the Elevation API
  3. Easily display the route on our watch using the Google Maps API for Android Wear

Add Android Wear Module to Project

First, we need to add a new Android Wear module to our existing project. In Android Studio, select FileNewModule and select Android Wear Module.
Screen Shot 2015-06-09 at 2.34.30 PM.png

On the Next screen, give your module a name. In this example, we called it “Code the Road” and gave it a module name of “wear.” This name is important for our gradle build files, so more on this shortly. Note: be sure the SDK is version 21 or higher. Start with the default blank activity. We will be modifying this activity, so it is important to get started.

Once the module is added to the project, it is important that we configure the build.gradle files, so the modules compile and the final application bundles everything correctly. In the build.gradle for the “Module wear”, ensure that the following dependencies are set. It is important that you reference Google Play Services version 7.5.0 or greater to ensure the Maps API for Android is available.
dependencies {
   compile fileTree(dir: 'libs', include: ['*.jar'])
   compile 'com.google.android.support:wearable:1.2.0'
   compile 'com.google.android.gms:play-services:7.5.0'
}
Now that we have these initial configurations in place, please reference the guide to create an initial MapsActivity within your Android Wear module. This will initialize a basic Google Map within your Wear App.
dependencies {
   compile fileTree(dir: 'libs', include: ['*.jar'])
   wearApp project(':wear')
   compile 'com.google.android.gms:play-services:7.5.0'
   compile 'com.android.support:appcompat-v7:22.1.1'
   compile 'com.firebase:firebase-client-android:2.3.0+'
}
Debugging. At this point, it is important that you are able to successfully test and debug your Android Wear application. For our development, we debugged primarily using an actual Android wear device using the steps in Debugging over bluetooth. You can also debug using the emulator by following the steps in Set up and Android Wear Virtual Device. Take your time getting this setup and working correctly before continuing.

Set the start and end location
Now that we have our basic Google Map setup on the Android Wear module, let’s jump back to the mobile application code. The first thing we want to do is get the start and stop locations for our planned bike route. To do this, we add an OnMapClickListener to the map and capture the start and stop locations on the map.
// Setup onclick event listener for the map
mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {

   @Override
   public void onMapClick(LatLng point) {

       // Logic to add markers to the map on click.
       … 

       LatLng origin = (LatLng) bikeMarkerList.get(0).getPosition();
       LatLng dest = (LatLng) bikeMarkerList.get(1).getPosition();

       // Get URLs for the Directions API
       String url = getDirectionsUrl(origin, dest);

       // Create an AsyncTask to fetch JSON data from Directions API
       DirectionsDownloadTask downloadTask = new DirectionsDownloadTask();

       downloadTask.execute(url);
   }
});
Construct the request to the Directions API
Ultimately, we want to draw a polyline to demonstrate the route for a “biking mode” between our start to end locations. But first, we need to send a request to the Directions API in order to retrieve the route for our trip. We construct a URL to the Directions API web service:
// Building the url to the Directions API web service
private String getDirectionsUrl(LatLng origin,LatLng dest){

// String for the origin of the route
String str_origin = "origin="+origin.latitude+","+origin.longitude;

// String for the destination of the route
String str_dest = "destination="+dest.latitude+","+dest.longitude;

// Enable bicycling mode
String mode = "mode=bicycling";

// We build the parameters for our URL string
String parameters = str_origin+"&"+str_dest+"&"+mode;

// Construction of the entire URL to the Directions API. 
// IMPORTANT: Notice how we proxy the requests through a web server to
// protect your API key.
String url = "https://<YOUR_PROXY_SERVER>/directions?"+parameters;

return url;
}
Parse the Directions API JSON response
Within the onPostExecute function of our DirectionsDownloadTask (see AsyncTask), we created a helper class to parse the JSON response and grab the overview_polyline value. We then “decode” this polyline and store the lat / lng result to a global List variable (more on this later). Note: For your application, you may choose to traverse all the routes and legs to capture every lat / lngs pair. For demo purposes, we just used the overview_polyline.
protected void onPostExecute(String directionsJSON) {
   super.onPostExecute(directionsJSON);

   try {
       JSONObject jObject = new JSONObject(directionsJSON);
       DirectionsJSONParser parser = new DirectionsJSONParser();

       // Parse the data. directionsResult is a List of decoded poly
       directionsResult = parser.parse(jObject);

       // This is the encoded polyline to pass on the Elevation API call
       String overviewPolyline = parser.getEncodedPoly(jObject);

       //Now that we have route, we need to get the Elevation data.
       ElevationDownloadTask elevationDownloadTask = new
  ElevationDownloadTask();
       String url = getElevationUrl(overviewPolyline);

       elevationDownloadTask.execute(url);

   } catch (Exception ex) {
       ex.printStackTrace();
   }
}

Construct the Request to the Elevation API

Now that we have the route for the trip, we can make a call to the Elevation API using the encoded polyline to get the corresponding elevations for each lat / lng pair. Similar to the Directions API logic, we make an Elevation API call using an AsyncTask. Below is an example of how we construct the URL.
private String getElevationUrl(String encodedPolyline) {
   // Build parameters to the web service
   String parameters = "locations=enc:" + encodedPolyline;
   // Build URL to the web service
   String url = "https://<YOUR_PROXY_SERVER>/elevation?" + parameters;
   return url;
}
Parse the Elevation API JSON response and draw on map
We were planning to render the polyline with a corresponding elevation chart below the map. Although this would be very useful, we wanted to display elevation information on a much smaller interface like a watch. With less screen real estate, we decided to color the polyline segments on the map based on elevation change, where green equals downward elevation and red equals upward elevation. To accomplish this, we added the following logic to our ElevationDownloadTask:
  1. First, we parse the JSON and add the corresponding elevations to a global List elevation result.
  2. Second, we iterate through all the route segments (directionsResult) and to get the lat / lng pairs and elevation difference.
  3. Using a helper function, we calculate the color of the segment based on elevation change (green → red or red → green)
  4. Finally, we add the polyline to the map and pass the polyline information over to the Wearable device using the PutDataMapRequest.
protected void onPostExecute(String elevationJSON) {
   super.onPostExecute(elevationJSON);

   try {
       JSONObject jObject = new JSONObject(elevationJSON);
       ElevationJSONParser parser = new ElevationJSONParser();

       // Start parsing data
       elevationResult = parser.parse(jObject);

       // We use later send this to the Wearable device to recreate poly.
       String routeForWearable = "";

       // Go through all segments
       for (int i = 1; i < directionsResult.size(); i++) {
           LatLng prevPosition = directionsResult.get(i - 1);
           LatLng position = directionsResult.get(i);
           double prevElevation = Double.valueOf(elevationResult.get(i - 1));
           double elevation = Double.valueOf(elevationResult.get(i));

           double elevationDiff = elevation - prevElevation;

           // Get color based on elevation change.
           // Green --> Red (and vice versa) gradient logic.
           int color = getColorByElevChange(elevationDiff);

           // Create the polyline segment.
           PolylineOptions polyOptions = new PolylineOptions()
                   .add(prevPosition, position)
                   .color(color)
                   .width(8);

           // Draw polyline for segment
           mMap.addPolyline(polyOptions);

           // We maintain this String variable to pass over via the DataApi to
      the Wearable
           // app. From there we parse the response and also create the polyline
      there.
           routeForWearable += prevPosition.latitude + "," + 
      prevPosition.longitude + ":"
                   + position.latitude + "," + position.longitude + ":"
                   + color + "|";
       }

       // Here we send over the polyline string to the Wearable device.
       PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/route");
       putDataMapReq.getDataMap().putString(ROUTE_TAG, routeForWearable);
       PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
       PendingResult pendingResult =
               Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq);
   } catch (Exception e) {
       e.printStackTrace();
   }
}
code-the-road-elevation.jpg

Display the Route on Our Watch

Setup WearableListenerService. The final step of the app is to create a WearableListnerService on the Wearable app to grab the polyline information passed in the previous step. Within this listener service, we have an onDataChanged function to capture the routeForWearable string and use that information to create a similar polyline on the Wearable application.
public void onDataChanged(DataEventBuffer dataEvents) {

… // Initialization logic

for (DataEvent event : dataEvents) {
   if (event.getType() == DataEvent.TYPE_CHANGED) {
       // DataItem changed
       DataItem item = event.getDataItem();
       if (item.getUri().getPath().compareTo("/route") == 0) {
           DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
           String routeFromMobile = dataMap.getString(ROUTE_TAG);

           // Go back to main UI thread and draw polyline
           // Broadcast message to wearable activity for display
           Intent messageIntent = new Intent();
           messageIntent.setAction(Intent.ACTION_SEND);
           messageIntent.putExtra(ROUTE_TAG, routeFromMobile);
           LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);

       }
   } else if (event.getType() == DataEvent.TYPE_DELETED) {
       // DataItem deleted
   }
}
}
It is important to note that we need to use the LocalBroadcastManager in order to pass control from the WearableListenerService back to the main UI thread in order to render the polylines. To accomplish this, we setup the following nested class in our MapsActivity on the Wearable app.
public class MessageReceiver extends BroadcastReceiver {
   @Override
   public void onReceive(Context context, Intent intent) {
       String routeFromMobile = intent.getStringExtra(ROUTE_TAG);
       // Draw the elevation polyline based on parsing the String.
       drawElevationPolyline(routeFromMobile);
   }
}

It's Been a Long Trip

This is our fourth and final blog post in our Code the Road App blog series—thank you for reading and following along! Now that you have a working Android application, you can continue to extend it with new features like creating an interactive chart showing elevation that allows users to hover over the chart to see the corresponding map location or allowing location sharing with friends. You can also build a web app to retrieve your location and check-in history from Firebase. This is only the start of what you can do with the Google Maps APIs, so use your imagination and build some amazing applications!

Code the Road: Our upcoming visit to Walt Disney World Resort

Code the Road Full Bus No Text.png

We’re pulling into the last Code the Road stop on Friday, June 26 where we’ll be visiting Epcot to celebrate our ten year anniversary and to demonstrate how technology brings the guest experience at Walt Disney World Resort to life.
Day1_Select5.JPG

Epcot guests will be able to visit the bus, located in in Future World West near Innoventions between 9am and 9pm to see how our customers, like Walt Disney World Resort, are using Google Maps APIs to build engaging, location-rich applications for their users and customers.
Disney.jpg

While there, we will be hosting an invitation-only event with the Disney team for 40 girls from Tech Sassy Girlz, an Orlando-based non-profit program designed to provide girls in grades 6 through 12 exposure and access to STEM fields and careers.
DSC_0819-2.jpg

While at Epcot, the girls will experience a full day of learning and adventure including engaging talks from the Disney and Google teams in the morning and educational sessions in the afternoon. The event will demonstrate how technology and engineering create engaging, memorable experiences, like navigating the Walt Disney World Resort with the My Disney Experience app.

If you’re planning to be at Epcot on Friday, stop by to see us at the bus.

Posted by Ashley Smith, Developer Marketing, Google Maps APIs

Code the Road: A Visit with the Chicago Department of Transportation Recap

After meeting with developers in Chicago last week, we paid a visit to the Chicago Department of Transportation who is currently using the Google Maps APIs to coordinate their public projects. We parked our bus in front of their building in downtown Chicago to let all passersby come into the bus and look at how the DOT is executing the great infrastructure changes happening in their city.
C11C1345.jpg

We had the pleasure of meeting with William Cheaks, First Deputy Commissioner, Chicago Department of Transportation and Larry Olszak, Director of IT, Chicago Department of Transportation, to talk to us about how they use Google Maps APIs to help manage the city of Chicago’s infrastructure projects.
C11C1475.jpg


A032_C019_0611QJ.0000577.jpg

We also had a great surprise at the event—our friends at grubHub stopped by to bring the crew some delicious donuts. It was sweet treat to brighten everyone’s day. Even better, they used the Google Maps APIs delivered to us!
A033_C001_06115A_S000.0001783.jpg

We’re looking forward to seeing you at one of our future stops. Join us in McLean, Virginia at Hilton Worldwide Headquarters on June 22 from 7am to 10am for a tour of the bus.

Posted by Hena Haines, Associate Product Marketing Manager, Google Maps APIs

Code the Road: Chicago Developer Meetup Recap

After an amazing day with Harley-Davidson riders in Milwaukee, we made it to Chicago for our second developer meetup. We had a blast showing the developers the Code the Road bus in the middle of downtown Chicago. We hope all who attended had a fantastic time.
C11C0094.jpg

Before the event even started we hitched a ride with a few maps developers taking Lyfts into the Chicago office. We spoke to their drivers about topics ranging from the future of maps to who they wish they could carpool with in a Lyft. Once at the event, attendees had the opportunity to explore the bus, pick up pegman stickers (complete with the Lyft mustache), and meet other developers before heading up to hear four technical talks.
C11C0126.jpg

I was up first (traveling all the way from Sydney) to discuss using Google Maps APIs in Android apps. I outlined ways to use custom markers, colors, and animations to make maps more focused and easier for users to navigate.
C11C0173.jpg

Next up was Ed Boiling, Google Maps for Work Solution Architect, who demonstrated how to use Google Cloud Platform to manage, serve, visualize , and analyze location data.
C11C0270.jpg

We, then, welcomed Patrick Skoglund Geospatial Lead from SADA Systems. He outlined how using the Google Maps toolset can help large companies solve complex problems.
C11C0191.jpg

We ended the night hearing from Kyle Madsen, Android developer at Lyft. Kyle gave a great overview of his experience with Google Maps APIs at Lyft. He described how the ease of implementation using Google Maps APIs helped them to focus on their core Lyft application while relying on Google Maps for the navigation components.
C11C0252.jpg

Next up, we’re on our way to New York to meet with Sesame Street and a few of our youngest fans. We’ll also be hosting our last developer meetup on June 18, so sign-up to join us in New York.

We look forward to seeing you in New York or one of our future stops on the road!

Posted by Florian Bertele, Product Manager, Google Maps APIs

Code the Road Android App: Checking in using the Places API



Editor’s Note: We are (literally) coding a mobile app on the road as part of our cross-country road trip, Code the Road. If you haven’t read our previous posts in the series, be sure to check out Hitting the Road with the Google Maps Android API and Store Your Locations in the Cloud with Firebase to get started. Follow along as we build our app on the road and read more from David McClusky, Maps Solutions Architect at Google.

We’re now in the third week of our Code the Road journey and we’ve had a great time meeting with developers and customers across the country. In this third post in our app blog series we will extend the capabilities of our mobile application by adding the ability to ‘check-in’ to places we visit along the way using the Google Places API for Android. If you haven’t read our earlier posts, checkout our posts on building a mobile map with the Google Maps Android API and connecting it to the cloud.

Our Code the Road bus is now visiting New York City, so we thought it would be useful to update our app and add the capability to view nearby places, select places and check-in, and then store those locations in the cloud for future reference. To do this we’ll integrate the Google Places API for Android.

We’ll start by utilizing the Place Picker—an out-of-the-box user interface that allows your app to quickly connect to Google’s database of hundreds of millions points of interest.

Prerequisites

Before you get started, make sure you have the latest version of Android Studio installed and your project includes the Google Play services component. In addition, you must have an API Key for Google Places configured with your app’s digital certificate. For more information on creating a Places API key read the documentation. You should have also completed the first two steps in our blog series, as we’ll be utilizing our existing connection to Firebase to store our check-ins in the cloud.

Adding the Place Picker to Your Application

The Place Picker provides a UI dialog that displays an interactive map and a list of nearby places. When you activate the Place Picker, it attempts to identify the your current location, and will also provide a list of alternative suggestions. We’ll add some code to our app so when we select a place, we’ll add a marker to the map and store the location in the cloud.
It’s very easy to add the Place Picker to your application, as Google handles all of the user interface elements for you.

The first thing we’ll do is add a button to our app on the action bar to launch the place picker. View the documentation for adding an action button.

To launch the Place Picker from our action button, we will construct an intent in the onOptionsItemSelected() function:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
 // Handle presses on the action bar items
 switch (item.getItemId()) {
   case R.id.action_search:
     try {
        PlacePicker.IntentBuilder intentBuilder = new PlacePicker.IntentBuilder();
        Intent intent = intentBuilder.build(this);

        // Start the Intent by requesting a result, identified by a request code.
        startActivityForResult(intent, REQUEST_PLACE_PICKER);

     } catch (GooglePlayServicesRepairableException e) {
       GooglePlayServicesUtil.getErrorDialog(e.getConnectionStatusCode(), 
       this, 0);
     } catch (GooglePlayServicesNotAvailableException e) {
       Toast.makeText(this, "Google Play Services is not available.",
Toast.LENGTH_LONG).show();
     }
     return true;

  default:
    return super.onOptionsItemSelected(item);
  }
}
After we select a place, we’ll need to identify which location was selected and display a marker on the map. First we will capture the result by creating the onActivityResult() function. We first obtain a reference to the place selected:
final Place place = PlacePicker.getPlace(data, this);
And then extract the various details about the place:
final CharSequence name = place.getName();
final CharSequence address = place.getAddress();
final CharSequence phone = place.getPhoneNumber();
Finally we can add a marker to the map:
mMap.addMarker(new MarkerOptions()
                   .position(place.getLatLng())
                   .title(name.toString())
Here is what the complete function in our app looks like:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {

 if (requestCode == REQUEST_PLACE_PICKER) {
    // This result is from the PlacePicker dialog.

    if (resultCode == Activity.RESULT_OK) {
       /* User has picked a place, extract data.
          Data is extracted from the returned intent by retrieving a Place 
          object from the PlacePicker.
         */
         final Place place = PlacePicker.getPlace(data, this);

         /* A Place object contains details about that place, such as its name, 
         address and phone number. Extract the name, address, phone number, 
         place ID and place types.
          */
         final CharSequence name = place.getName();
         final CharSequence address = place.getAddress();
         final CharSequence phone = place.getPhoneNumber();
         final String placeId = place.getId();
         String attribution = PlacePicker.getAttributions(data);
         if(attribution == null){
            attribution = "";
         }

         // Update data on map
         mMap.addMarker(new MarkerOptions()
            .position(place.getLatLng())
            .title(name.toString())
           );

         // Print data to debug log
         Log.d(TAG, "Place selected: " + placeId + " (" + name.toString() + ")");


       }
   } else {
       super.onActivityResult(requestCode, resultCode, data);
   }
   // END_INCLUDE(activity_result)
}

After the marker is added to the map, we can easily trigger turn-by-turn navigation by selecting the navigation icon in the bottom-right of the map to launch directions in Google Maps mobile.

Save the Location to the Cloud

Checking in to the places we’ve visited becomes even more useful if we are able to store those check-ins for future reference. Since we’ve already connected our app to the cloud using Firebase, it will be very easy to use that same connection to store our check-ins. After adding the marker to the map, we simply add the following lines of code to our function above to also save the location and id of the place to Firebase.
Map mLocation = new HashMap();
mLocation.put("timestamp", mLastUpdateTime);

Map  mCoordinate = new HashMap();
mCoordinate.put(“latitude”, place.getLatLng().latitude);
mCoordinate.put(“longitude”, place.getLatLng().longitude);
mLocation.put("location", mCoordinate); 

mLocation.put("place_id", place.getId());
myFirebaseRef.push().setValue(mLocation);

That’s all there is to it! We now have an app that, in addition to storing our location history in the cloud, also lets us record a list of all the places we’ve visited while on the road. Stay tuned for future blog posts where we will continue to extend our app’s functionality with features such as directions and support for wearable devices.

Code the Road: Hitting the Road with Harley-Davidson


Monday, June 8, was a great day in Milwaukee at Harley-Davidson where we met with the local H.O.G, chapters for a motorcycle ride to the Harley-Davidson Museum across town. We stopped in Milwaukee on our road trip with our Code the Road bus to celebrate the great work Harley-Davidson has done building the Harley-Davidson Ride Planner app using the Google Maps APIs.
IMG_3112.JPG

Upon arriving in Milwaukee, we were greeted by the most accommodating and friendly group of people as we lined up for our ride through the streets of Milwaukee. Although a rainy start, our riders didn’t let it dampen their spirits as we were led by some of the most beautiful motorcycles in the world. As far as we could see was a line of motorcycles leading the bus to the Harley-Davidson Museum—expertly escorted by Milwaukee police officers.

We captured some great footage of the ride from above with our drone and attached camera.

Our ride ended at the Harley-Davidson museum with tours of the Code the Road bus, a fun band, and refreshments for all the riders. We had local press outlets in attendance as well as many tourists visiting the museum. It was great to see the bus and the bikers that joined us on the local news channels that evening.
IMG_20150608_150055.jpg

The team had a blast driving a beautiful blue 2015 Softail Slim Harley-Davidson on a stationary treadmill and we even got to check out Harley-Davidson’s very first electric motorcycle, the LiveWire.

Our Code the Road postcard station is still going strong and we are enjoying seeing everyone send postcards home to document their visit to the bus.
5D.00_10_51_00.Still002.jpg

We’re rolling on and headed to New York. Don’t forget to signup for our New York Developer Meetup on Thursday, June 18.

Posted by Ed Boiling, Solutions Architect, Google Maps for Work

Harley-Davidson Ride Planner uses Google Maps APIs to help motorcyclists cruise the highways and byways



Editor’s note: Today’s guest blogger is Heidi Skinner, Social Strategy Manager at the Harley-Davidson Motorcycle Company. Read how Harley-Davidson uses Google Maps APIs to help motorcyclists plan and share road trips. Harley-Davidson is one of many customers sharing their story as part of our cross-country road trip, Code the Road.

Hitting the open road on your motorcycle isn’t what it used to be—it’s better, thanks to the Harley-Davidson Ride Planner mobile app and website that use the Google Maps APIs to help you plan trips, share them with others and get the most out of your route along the way. It's one of the many ways Harley-Davidson helps more people ride more miles.

Motorcyclists are a varied bunch. Some like short drives through urban centers, while others favor long rides through mountain ranges. They plan their rides with the same care as they plan their honeymoons or vacations. On the Ride Planner website, powered by the Google Maps JavaScript API, they can build their routes using Google Maps and choose the kinds of roads they favor, including Harley-Davidson Great Roads, which are some of the best roads in the country to ride on as rated by other Harley-Davidson riders.

The Google Places API lets them add important places—gas stations, hotels and attractions. We also use it to call out relevant places people might want to visit, like the hundreds of Harley-Davidson dealerships across the country that offer official Harley-Davidson merchandise, riding gear and parts & accessories as well as great restaurants, landmarks, campgrounds and other points of interest.
RidePlannerWeb2.png

RidePlannerWeb4_WithPlacesOptions.png

After people build their trips, the iOS and Android apps grab the information. The Android app uses the Google Maps API for Android to display the map, route and turn-by-turn directions. The Google Places API for Android lets people see and change their planned stops and attractions. The Google Maps SDK for iOS and Google Places SDK for iOS do the same thing for our iOS app, providing the same user experience across both platforms.
RidePlannerMobile1.jpg

People can share their rides as well. Members of the Harley Owners Group (H.O.G.) plan rides for as many as 500 people, who follow the route on the mobile apps. The apps also tie into mobile device GPS systems for navigation, and motorcyclists can export rides using GPX format into the built-in GPS on their Harley-Davidson motorcycles.

The app has been a huge success—people have planned hundreds of thousands of rides on the website, and the apps are each downloaded between 3,000 and 5,000 times a month. To help celebrate this momentum, we’re excited to be part of the Code the Road road trip with Google.

On June 8, members of local H.O.G. groups drove their Harleys alongside the Code the Road bus from our headquarters in Milwaukee to the nearby Harley-Davidson Museum. At the museum, we celebrated with a big party, including live music, tours of the bus and museum discounts for attendees. It was a great time with a great partner.

Code the Road Android App: Store your location data in the cloud with Firebase



Editor’s Note: We will be (literally) coding a mobile app on the road as part of our cross-country road trip, Code the Road. If you haven’t read our previous posts in the series, be sure to check out our first blog post, Code the Road: Hitting the Road with the Google Maps Android API to get started. Follow along as we build our app on the road and read more from Dong Sun, Maps Deployment Engineer at Google.

As we enter week two of our Code the Road journey and travel across the U.S., we thought: “What would be a better way to remember this amazing trip than by logging and saving our location history to the cloud?” In this post we will extend and build upon the Google Maps application we started in week 1 by:

  • Logging locations using the fused location provider, a simple API bundled with Google Play services, that provides high accuracy and low battery usage.
  • Saving and retrieving these locations using Firebase, a powerful application platform that enables real-time features without the hassle of networking, scaling, or complicated server code.
  • Displaying these recorded locations as markers on the map.

Step 1: Setup Firebase

Set up Firebase and data access. The first thing you need to do is set up Firebase, which will be used to store and retrieve your locations. Get started with the following steps:

  • Add the Firebase Android SDK—We used Firebase to store and retrieve location data. Follow this step-by-step guide to add Firebase to your project. Create a Firebase account, install the Firebase SDK, and set up the needed internet permission in your AndroidManifest.xml.

Firebase location data structure. Along with the latitude and longitude information, we will include a timestamp for each saved location. By saving the timestamp, it enables our application to aggregate the data daily, monthly, or any other specified time period. When this data is pushed into Firebase, it will appear as follows in the Firebase dashboard:
firebase-schema.png

Step 2: Capture Location and Save to Firebase

Now that we have Firebase ready and configured, our app needs to get both the initial location as well as location updates to save to Firebase.

Add start and stop logging buttons. We will add two buttons on the map application for starting and stopping location logging actions. For details on how to add buttons to the layout, please reference this link.
code-the-road-firebase-01.jpg
Request location permission. To get the user location, we required additional permissions. We need to add the following uses-permission elements to the manifest file. These settings control the accuracy of the current location.
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Implement necessary interfaces. To ensure the app does not become unresponsive when capturing the device’s location, we use an asynchronous process by implementing ConnectionCallbacks, OnConnectionFailedListener, and LocationListener on our MapsActivity. You will need to implement the required methods in the interfaces.
public class MapsActivity extends FragmentActivity
    implements GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener, 
    LocationListener {
}
Connect to Google Play Services. In our activity's onCreate() method, we created an instance of the Google API Client using GoogleApiClient.Builder and added the LocationServices API to the client.
protected synchronized void buildGoogleApiClient() {
  mGoogleApiClient = new GoogleApiClient.Builder(this)
      .addConnectionCallbacks(this)
      .addOnConnectionFailedListener(this)
      .addApi(LocationServices.API)
      .build();
}
Get initial device location. Whenever the client is connected to the service, GoogleApiClient.ConnectionCallbacks are called. The OnConnected() method will be invoked asynchronously when the connect request has successfully completed. Once the client is connected, we use the the fused location provider to get the last known location and zoom the map to that location.
@Override
public void onConnected(Bundle bundle) {
  mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
       mGoogleApiClient);
  mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
      new LatLng(mLastLocation.getLatitude(), 
      mLastLocation.getLongitude()),      
                 MAP_ZOOM_LEVEL));
}
Set up location request and update interval. We need to create and configure a LocationRequest object. You will need to set the location request preferences that are appropriate for your application. For example, fitness apps will want high accuracy with small intervals, but an asset tracking app might require lower accuracy with larger intervals.
public static final long  UPDATE_INTERVAL_IN_MS = 120000; 
public static final long FASTEST_UPDATE_INTERVAL_IN_MS =
UPDATE_INTERVAL_IN_MS / 4;
protected LocationRequest mLocationRequest;
Request regular location updates. When the “START” button is clicked, we use the method below to start regular location updates.
private void startLogging() {
  mLocationRequest = new LocationRequest();
  mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MS); 
  mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MS);
  mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
  if (mGoogleApiClient.isConnected() && !mRequestingLocationUpdates) {
       mRequestingLocationUpdates = true;
       startLocationUpdates();
   }
}
 
protected void startLocationUpdates() {
    LocationServices.FusedLocationApi.requestLocationUpdates(
           mGoogleApiClient, mLocationRequest, this);
}
The fused location provider invokes the LocationListener.onLocationChanged() callback method when a new location is detected by Google Play Services. We get each location, and save the system time in UTC, latitude and longitude to Firebase.
@Override
public void onLocationChanged(Location location) {
   mCurrentLocation = location;
   DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
   dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
   Date date = new Date(); 
   mLastUpdateTime = dateFormat.format(date).toString();

   saveToFirebase();

   // Retrieve saved locations and draw as marker on map
   drawLocations();

   // Update UI to draw bread crumb with the latest bus location.
   mMap.clear();

   LatLng mLatlng = new LatLng(mCurrentLocation.getLatitude(), 
   mCurrentLocation.getLongitude());
   MarkerOptions mMarkerOption = new MarkerOptions()
      .position(mLatlng)
      .title(mLastUpdateTime))
      .icon(BitmapDescriptorFactory.fromResource(R.drawable.code_the_road_small));

   Marker mMarker = mMap.addMarker(mMarkerOption); 
}
Save location info to Firebase. The Firebase Android SDK offers offline capability so Firebase applications work even when network connection is temporarily lost. Learn more about the offline capabilities of Firebase. We first need to create a connection to our Firebase instance:
myFirebaseRef = new Firebase("<YOUR-FIREBASE-APP>");
We then use the Firebase push() function that generates a unique ID every time a new child is added to the specified Firebase reference.
private void saveToFirebase() {
    Map mLocations = new HashMap();
    mLocations.put("timestamp", mLastUpdateTime);
    Map  mCoordinate = new HashMap();
    mCoordinate.put(“latitude”, mCurrentLocation.getLatitude());
    mCoordinate.put(“longitude”, mCurrentLocation.getLongitude());
    mLocations.put("location", mCoordinate); 
    myFirebaseRef.push().setValue(mLocations);
}

Step 3: Fetch Saved Locations and Draw on Map

Every location saved after the “START” button was clicked is retrieved from Firebase and drawn on the map as a marker. For better query performance, we added an index rule to index the data element “timestamp.” The app stops logging locations when the user clicks the “STOP” button.
private void drawLocations() {
  // Get only latest logged locations - since 'START' button clicked
  Query queryRef = 
  myFirebaseRef.orderByChild("timestamp").startAt(startLoggingTime);
  // Add listener for a child added at the data at this location
  queryRef.addChildEventListener(new ChildEventListener() {
    LatLngBounds bounds;
    LatLngBounds.Builder builder = new LatLngBounds.Builder();
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String s) {
      Map  data = (Map ) dataSnapshot.getValue();
      String timestamp = (String) data.get("timestamp");
      // Get recorded latitude and longitude
      Map  mCoordinate = (HashMap)data.get("location");
      double latitude = (double) (mCoordinate.get("latitude"));
      double longitude = (double) (mCoordinate.get("longitude"));

      // Create LatLng for each locations
      LatLng mLatlng = new LatLng(latitude, longitude);

      // Make sure the map boundary contains the location
      builder.include(mLatlng);
      bounds = builder.build();

      // Add a marker for each logged location
      MarkerOptions mMarkerOption = new MarkerOptions()
          .position(mLatlng)
          .title(timestamp)
          .icon(BitmapDescriptorFactory.fromResource(R.drawable.measle_blue));
      Marker mMarker = mMap.addMarker(mMarkerOption);
      markerList.add(mMarker);

      // Zoom map to the boundary that contains every logged location
      mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds,
         MAP_ZOOM_LEVEL));
     }

      //Override other abstract methods for addChildEventListener below
      …
      });
    }
code-the-road-firebase-02.png
What’s next?
Now that we can record locations on demand, we could clean up our GPS locations using the snap-to-road function in the Roads API—just remember to proxy the use of this web service through your server. We could also further develop the application to share our location with friends or calculate the distance traveled. In the next few blog posts, we’ll expand our app by adding check-ins using the Places API and adding support for wearable devices.

Lyft connects drivers and riders with Google Maps APIs



Editor’s note: Today’s guest blogger is Vishay Nihalani, Product Manager at the ride-sharing service Lyft. Read how Lyft uses Google Maps APIs to help riders save money and get to their destinations faster. Lyft is one of many customers sharing their story as part of our cross-country road trip, Code the Road.

We know a lot about road trips—people have taken tens of millions of rides with us from the steep hills of San Francisco to the city streets of Chicago. Ride-sharing today is an intensely competitive business. If you’re going to succeed, you’ve got to connect riders and drivers simply and conveniently and make the experience delightful from beginning to end. That’s why we chose Google Maps and the Google Maps APIs. We started using them with our internal operations tools, and when we saw how valuable and scalable they were, we worked with Google to launch their services in our customer-facing apps.

The Google Maps Android API creates our Android app’s main interface—it’s the map you see when users log in. Our users can drop pins on a map or enter their location manually to indicate pick-up and drop-off locations. Through reverse geocoding, the app will recognize locations and determine an address automatically based on where the pin is dropped. We also help people find their locations by using the Google Places Search API. People often think about the places they want to go by name—for example, they’ll enter “Cliff House” rather than the restaurant’s street address. The Google Places Search API allows a passenger to enter the name of a destination.

When customers use our app, they expect there’ll be a car waiting for them and they’ll get to their destination as quickly as possible. They need to know when they’ll be picked up and when they’ll get where they want to go. We make it easy for our riders to quickly see this information right in the app.

Lyft now has more than 100,000 drivers and delivers more than two million rides per month in 65 cities. We’ve seen more than 500% growth in rides and revenue since last year, thanks in large part to Google Maps. And we’re exploring ways to make Lyft even better by using maps to improve the ride options we offer—for example, by expanding our new Lyft Line option for riders going in the same direction.

Google has been a great partner and has really contributed to our success, so we’re pleased to participate in Code the Road as Google celebrates mapping innovation and fun on its trip across America. Kyle Madsen, Android Developer at Lyft, presented at the Chicago developer meetup on June 9th. It was a great crowd and an exciting event. We hope to see you on the road!

Code the Road: Boulder Developer Meetup Recap

Screen Shot 2015-06-07 at 9.13.17 PM.png

For our first of three developer meetups, we stopped in Boulder, CO—after traveling from Arches National Park. We were thrilled to have a packed house at our first Code the Road meetup, with our attendees maxing out our 80-person capacity. We held four technical talks, an outdoor barbecue and met with many new friends. We hope everyone who attended enjoyed the evening.

First up was Google Maps APIs Engineering Manager James McGill who gave a talk titled Using the Google Maps APIs in your iOS or Android App.
A012_C046_060447.0000107.jpg
James McGill, Engineering Manager Google Maps APIs

Next, we were happy to welcome Chase Brammer, Head of iFit Product Development from our friends at iFit. Chase gave a talk about how the iFit technology brings hikes into your home using the Google Maps APIs.
A012_C049_06042X.0000052.jpg
Chase Brammer, Head of iFit Product Development, ICON Health and Fitness

I was up next outlining how you can use the Google Maps APIs with Google Cloud Platform to supercharge your apps. Closing out our round of tech talks was Nirav Patel, Director of Geospatial Solutions at Dito who outlined how they helped their client, Kocomojo, optimize Bluetooth iBeacon Technology using the Google Maps APIs.
A007_C028_06041W.0000691.jpg
The BBQ during the Boulder Developer Meetup

We’re looking forward to sharing a recap of our visit to Milwaukee at the Harley-Davidson Museum in the afternoon, and if you can make it, register for our Chicago and New York developer meetups. See you on the road!

Posted by Sean Wohltman, Geospatial Scientist, Google Maps for Work