Advertisement
Android SDK

Android SDK Quick Tip: Launching Maps In-App

by

This Quick Tip will show you how to programmatically launch in to the Google Maps application. You will learn to load Maps with a precise position, a location name, and with a search query. As a bonus tip, you’ll see how to load a Street View at a particular location, with the “camera” aimed the direction you want.

Step 1: Preparation

We’ll begin this tutorial using the Phrasebook application we last built upon in the Enabling the Android “Move To SD Card” Feature tutorial on this site. The Phrasebook application displays some text and images based upon the user’s locale setting. One of these images is a map-a perfect place to try this tip. If you’d like to follow along, you can download the source code from that tutorial. The final code is also available on Google Code Hosting.

Localization with Map view

Step 2: Adding the Click Handler

We begin by adding a click handler to the existing ImageView that displays a map image based upon the user’s locale. In the PhrasebookActivity.java class file, add the following code within the onCreate() method:

ImageView map = (ImageView)findViewById(R.id.ImageViewMap);
map.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        // TODO: start map Activity here
    }
});

Step 3: Starting the Map Activity

Next, add some code to the click handler to read the location we want to display and send that data off to the Maps application. To do this, you can simply read the string resource, which will get a locale-specific string. (We’ll be adding the string resources in Steps 4-7.) Then use the specially-formatted string to create the appropriate Intent. Finally, use the Intent to launch the Maps app with a call to the startActivity() method, like so:

String geoUriString = getResources().getString(R.string.map_location);
Uri geoUri = Uri.parse(geoUriString);
Intent mapCall = new Intent(Intent.ACTION_VIEW, geoUri);
startActivity(mapCall);

In this way, when the user clicks the back button, they’ll be returned to the previous Activity. In this case, it’s your Activity so the user will be right back where they started. This is a great example of one of the advantages of the Activity model that Android uses.

Step 4: Displaying Maps by Latitude and Longitude

The Phrasebook application is now set up to load location resource strings and display the Map in the appropriate location. Let’s take things a step further by providing latitude, longitude and a zoom level value. For example, you could create a location string resource with the following location data:

geo:0,180?z=1

This data will load up the Map at a latitude of 0 (the Equator) and a longitude of 180 (middle of the Pacific). The map will be zoomed all the way out, so it’ll show the whole world:

Google World View

Step 5: Displaying Maps by Location Name Query

In addition to fixed-point locations, the Maps application can be launched to locations by name. The Maps application has support for a variety of different location name formats (addresses, countries, cities, landmarks, etc.). You should, however, remember that some names are quite ambiguous. For the Phrasebok application, it makes sense to seed the map using the country’s name.

For example, set the value to the map_location string in the French-speaking Belgium regional string resource file (values-fr-rBE/strings.xml) to:

"geo:0,0?q=Belgium"

Here, we searched for a country name. When launched, the Maps application will display the appropriate region of the world: Belgium!

Android Maps Belgium Map View

As we said, location names can be landmarks. For example, you could add the following location data to the Swiss-French string file (values-fr-rCH/strings.xml):

"geo:0,0?q=Matterhorn&z=8"

Again, the Maps application displays the appropriate map location--that of the Matterhorn in the Swiss Alps.

Matterhorn Map View

Note the placeholder values for latitude and longitude within the queries. This is simply part of the format of the request. A specific street address will also work with these queries.

Step 6: Displaying Maps with a Search Query

In typical Google Maps fashion, we can also do a search for places of interest within a certain vicinity. For example, we could add the following map query to the French/France string resource file (values-fr-rFR/values.xml):

"geo:0,0?q=Coffee Shops near Paris, France"

This query tells the Maps application to center the map in Paris, France and search for nearby coffee shops. Google Maps uses its own algorithms for deciding which shops to display. The user can drill down to see more shops.

Coffee Shops Map View

Step 7: Displaying a Location with Street View

Finally, you can also display the Street View at a particular location, if one exists. This next string goes in French Canadian string resource file (values-fr-rCA/strings.xml):

"google.streetview:cbll=46.813812,-71.207378&cbp=1,99.56,,1,-5.27&mz=21"

This location data tells the Maps application to display the Street View of the gardens along Rue des Jardins in Old Quebec:

Android Street View

The street view query needs a little more explanation. Street View is actually a different application from the Maps application on Android. The data passed to it matches the named parameters of the web version of Google Maps. The cbll parameter contains the the latitude and longitude information while the cbp parameter allows for some further Street View configuration:

1,yaw,,pitch,zoom

The yaw controls the direction you look in degrees clockwise from north and the pitch is in degrees from level, where up is negative (so 90 is looking straight down). The zoom is a multiplier, so 3 is a 3x zoom. The 1 and the double commas are required, but unused. Finally, the mz parameter provides the map zoom factor in case the user clicks on the “Go to Maps” option within Street View application. The easiest way to get the cbp parameters you desire is by using Google Maps in a regular web browser, navigating to the appropriate location and view, and extracting the Street View parameters from the link you can create to get to that location.

Conclusion

You now have the tools you need to easily launch in to the Google Maps or Street View applications on an Android device. You can load the map with a specific location, view a location by it’s name or address, and even perform a search near a location. Finally, you can load the Street View application to show a particularly interesting area of the world and adjust how it appears to users.

About the Authors

Mobile developers Lauren Darcey and Shane Conder have coauthored several books on Android development: an in-depth programming book entitled Android Wireless Application Development and Sams TeachYourself Android Application Development in 24 Hours. When not writing, they spend their time developing mobile software at their company and providing consulting services. They can be reached at via email to androidwirelessdev+mt@gmail.com, via their blog at androidbook.blogspot.com, and on Twitter @androidwireless.

Need More Help Writing Android Apps? Check out our Latest Books and Resources!

Buy Android Wireless Application Development, 2nd Edition  Buy Sam's Teach Yourself Android Application Development in 24 Hours  Mamlambo code at Code Canyon

Related Posts
  • Web Design
    HTML & CSS
    How to Add Branded Branch Locations to Google MapsMap thumb
    In this tutorial we'll walk through the process of creating a branded Google map for an imaginary client.Read More…
  • Code
    Android SDK
    Android SDK: Next StepsAndroid preview@2x
    In this series, we've begun learning how to develop Android applications from scratch. We started exploring the development tools, got acquainted with the basic elements in an application project, looked at user interface design, interactivity, resources, and data, and we've also took a closer look at what happens when your application is running. What we've covered so far should put you in a good position to get started creating functional Android applications, but Android has a lot more to offer so the range of possibilities is virtually endless. You may therefore struggle to choose what to learn next. In this part, we'll wrap up the series by pointing out some possible directions for future learning. After this, the final part will be a quiz on what we covered throughout the series.Read More…
  • Code
    Android SDK
    Android SDK: User Interface DesignAndroid preview@2x
    In this series we're learning about Android SDK development from scratch! In this tutorial, we will build a simple user interface layout with a few visual elements.Read More…
  • Computer Skills
    App Training
    Getting the Most From Maps in OS X MavericksMaps400
    Whilst OS X 10.9 Mavericks is reported to have some two-hundred changes, over 10.8 Mountain Lion, there’s really only a handful of immediately visible changes. Apple’s new Maps application is one of the most apparent. In this tutorial, I will show you how to get the most from the new Maps app. You’ll soon wonder how you ever managed without it.Read More…
  • Web Design
    HTML/CSS
    Linking Up the Google Maps and Flickr APIsGoogle map flickr retina
    Imagine you've made the beautiful map a client requested; adding all sorts of interesting markers, pop-ups, custom overlays and photos.  But then the client mentions they want to be able to add their own photos without either troubling you, or doing any coding themselves. This is where the Flickr API comes in very handy.Read More…
  • Code
    Android SDK
    Android SDK: Working with Google Maps - Displaying Places of InterestAndroid map preview retina
    With Google Maps in your Android apps, you can provide users with localization functions, such as geographical information. Throughout this series we have been building an Android app in which the Google Maps Android API v2 combines with the Google Places API. So far we have displayed a map, in which the user can see their current location, and we have submitted a Google Places query to return data about nearby places of interest. This required setting up API access for both services. In the final part of the series, we will parse the Google Places JSON data and use it to show the user nearby places of interest. We will also make the app update the markers when the user location changes. This is the last of four parts in a tutorial series on Using Google Maps and Google Places in Android apps: Working with Google Maps - Application Setup Working with Google Maps - Map Setup Working with Google Maps - Places Integration Working with Google Maps - Displaying Nearby Places This is a snapshot of the final app. 1. Process the Place Data Step 1 You will need to add the following import statements to your Activity class for this tutorial: [java] import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.util.Log; [/java] In the last tutorial we created an inner AsyncTask class to handle fetching the data from Google Places in the background. We added the doInBackground method to request and retrieve the data. Now we can implement the onPostExecute method to parse the JSON string returned from doInBackground, inside your AsyncTask class, after the doInBackground method: [java] protected void onPostExecute(String result) { //parse place data returned from Google Places } [/java] Step 2 Back in the second part of this series, we created a Marker object to indicate the user's last recorded location on the map. We are also going to use Markers to show the nearby places of interest. We will use an array to store these Markers. At the top of your Activity class declaration, add the following instance variable: [java] private Marker[] placeMarkers; [/java] By default, the Google Places API returns a maximum of 20 places, so let's define this as a constant too: [java] private final int MAX_PLACES = 20; [/java] When we create the Markers for each place, we will use MarkerOptions objects to configure the Marker details. Create another array instance variable for these: [java] private MarkerOptions[] places; [/java] Now let's instantiate the array. In your Activity onCreate method, after the line in which we set the map type, create an array of the maximum required size: [java] placeMarkers = new Marker[MAX_PLACES]; [/java] Now let's turn to the onPostExecute method we created. First, loop through the Marker array, removing any existing Markers. This method will execute multiple times as the user changes location: [java] if(placeMarkers!=null){ for(int pm=0; pm<placeMarkers.length; pm++){ if(placeMarkers[pm]!=null) placeMarkers[pm].remove(); } } [/java] When the app code first executes, new Markers will be created. However, when the user changes location, these methods will execute again to update the places displayed. For this reason the first thing we must do is remove any existing Markers from the map to prepare for creating a new batch. Step 3 We will be using Java JSON resources to process the retrieved place data. Since these classes throw certain exceptions, we need to build in a level of error handling throughout this section. Start by adding try and catch blocks: [java] try { //parse JSON } catch (Exception e) { e.printStackTrace(); } [/java] Inside the try block, create a new JSONObject and pass it to the result JSON string returned from doInBackground: [java] JSONObject resultObject = new JSONObject(result); [/java] If you look at the Place Search page on the Google Places API documentation, you can see a sample of what the query actually returns in JSON. You will see that the places are contained within an array named "results". Let's first retrieve that array from the returned JSON object: [java] JSONArray placesArray = resultObject.getJSONArray("results"); [/java] You should refer to the sample JSON result as we complete each section of this process - keep the page open in a browser while you complete the remainder of the tutorial. Next let's instantiate the MarkerOptions array we created with the length of the returned "results" array: [java] places = new MarkerOptions[placesArray.length()]; [/java] This should give us a MarkerOptions object for each place returned. Add a loop to iterate through the array of places: [java] //loop through places for (int p=0; p<placesArray.length(); p++) { //parse each place } [/java] Step 4 Now we can parse the data for each place returned. Inside the for loop, we will build details to pass to the MarkerOptions object for the current place. This will include latitude and longitude, place name, type and vicinity, which is an excerpt of the address data for the place. We will retrieve all of this data from the Google Places JSON, passing it to the Marker for the place via its MarkerOptions object. If any of the values are missing in the returned JSON feed, we will simply not display a Marker for that place, in case of Exceptions. To keep track of this, add a boolean flag: [java] boolean missingValue=false; [/java] Now add local variables for each aspect of the place we need to retrieve and pass to the Marker: [java] LatLng placeLL=null; String placeName=""; String vicinity=""; int currIcon = otherIcon; [/java] We create and initialize a LatLng object for the latitude and longitude, strings for the place name and vicinity and initially set the icon to use the default icon drawable we created. Now we need another try block, so that we can detect whether any values are in fact missing: [java] try{ //attempt to retrieve place data values } catch(JSONException jse){ missingValue=true; jse.printStackTrace(); } [/java] We set the missing value flag to true for checking later. Inside this try block, we can now attempt to retrieve the required values from the place data. Start by initializing the boolean flag to false, assuming that there are no missing values until we discover otherwise: [java] missingValue=false; [/java] Now get the current object from the place array: [java] JSONObject placeObject = placesArray.getJSONObject(p); [/java] If you look back at the sample Place Search data, you will see that each place section includes a "geometry" section which in turn contains a "location" section. This is where the latitude and longitude data for the place is, so retrieve it now: [java] JSONObject loc = placeObject.getJSONObject("geometry").getJSONObject("location"); [/java] Attempt to read the latitude and longitude data from this, referring to the "lat" and "lng" values in the JSON: [java] placeLL = new LatLng( Double.valueOf(loc.getString("lat")), Double.valueOf(loc.getString("lng"))); [/java] Next get the "types" array you can see in the JSON sample: [java] JSONArray types = placeObject.getJSONArray("types"); [/java] [tip] Tip: We know this is an array as it appears in the JSON feed surrounded by the "[" and "]" characters. We treat any other nested sections as JSON objects rather than arrays. [/tip] Loop through the type array: [java] for(int t=0; t<types.length(); t++){ //what type is it } [/java] Get the type string: [java] String thisType=types.get(t).toString(); [/java] We are going to use particular icons for certain place types (food, bar and store) so add a conditional: [java] if(thisType.contains("food")){ currIcon = foodIcon; break; } else if(thisType.contains("bar")){ currIcon = drinkIcon; break; } else if(thisType.contains("store")){ currIcon = shopIcon; break; } [/java] The type list for a place may actually contain more than one of these places, but for convenience we will simply use the first one encountered. If the list of types for a place does not contain any of these, we will leave it displaying the default icon. Remember that we specified these types in the Place Search URL query string last time: [text] food|bar|store|museum|art_gallery [/text] This means that the only place types using the default icon will be museums or art galleries, as these are the only other types we asked for. After the loop through the type array, retrieve the vicinity data: [java] vicinity = placeObject.getString("vicinity"); [/java] Finally, retrieve the place name: [java] placeName = placeObject.getString("name"); [/java] Step 5 After the catch block in which you set the missingValue flag to true, check that value and set the place MarkerOptions object to null, so that we don't attempt to instantiate any Marker objects with missing data: [java] if(missingValue) places[p]=null; [/java] Otherwise, we can create a MarkerOptions object at this position in the array: [java] else places[p]=new MarkerOptions() .position(placeLL) .title(placeName) .icon(BitmapDescriptorFactory.fromResource(currIcon)) .snippet(vicinity); [/java] Step 6 Now, at the end of onPostExecute after the outer try and catch blocks, loop through the array of MarkerOptions, instantiating a Marker for each, adding it to the map and storing a reference to it in the array we created: [java] if(places!=null && placeMarkers!=null){ for(int p=0; p<places.length && p<placeMarkers.length; p++){ //will be null if a value was missing if(places[p]!=null) placeMarkers[p]=theMap.addMarker(places[p]); } } [/java] Storing a reference to the Marker allows us to easily remove it when the places are updated, as we implemented at the beginning of the onPostExecute method. Notice that we include two conditional tests each time this loop iterates, in case the Place Search did not return the full 20 places. We also check in case the MarkerOptions is null, indicating that a value was missing. Step 7 Finally, we can instantiate and execute our AsyncTask class. In your updatePlaces method, after the existing code in which we built the search query string, start this background processing to fetch the place data using that string: [java] new GetPlaces().execute(placesSearchStr); [/java] You can run your app now to see it in action. It should display your last recorded location together with nearby places of interest. The colors you see on the Markers will depend on the places returned. Here is the app displaying a user location in Glasgow city center, UK: Perhaps unsurprisingly a lot of the places listed in Glasgow are bars. When the user taps a Marker, they will see the place name and snippet info: 2. Update With User Location Changes Step 1 The app as it stands will execute once when it is launched. Let's build in the functionality required to make it update to reflect changes in the user location, refreshing the nearby place Markers at the same time. Alter the opening line of the Activity class declaration to make it implement the LocationListener interface so that we can detect changes in the user location: [java] public class MyMapActivity extends Activity implements LocationListener { [/java] A Location Listener can respond to various changes, each of which uses a dedicated method. Inside the Activity class, implement these methods: [java] @Override public void onLocationChanged(Location location) { Log.v("MyMapActivity", "location changed"); updatePlaces(); } @Override public void onProviderDisabled(String provider){ Log.v("MyMapActivity", "provider disabled"); } @Override public void onProviderEnabled(String provider) { Log.v("MyMapActivity", "provider enabled"); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { Log.v("MyMapActivity", "status changed"); } [/java] The only one we are really interested in is the first, which indicates that the location has changed. In this case we call the updatePlaces method again. Otherwise we simply write out a Log message. At the end of the updatePlaces method, add a request for the app to receive location updates: [java] locMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, 100, this); [/java] We use the Location Manager we created earlier in the series, requesting updates using the network provider, at delays of 30 seconds (indicated in milliseconds), with a minimum location change of 100 meters and the Activity class itself to receive the updates. You can, of course, alter some of the parameters to suit your own needs. [tip] Tip: Although the requestLocationUpdates method specifies a minimum time and distance for updates, in reality it can cause the onLocationChanged method to execute much more often, which has serious performance implications. In any apps you plan on releasing to users, you should therefore limit the frequency at which your code responds to these location updates. The alternative requestSingleUpdate method used on a timed basis may be worth considering. [/tip] Step 2 Last but not least, we need to take care of what happens when the app pauses and resumes. Override the two methods as follows: [java] @Override protected void onResume() { super.onResume(); if(theMap!=null){ locMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, 100, this); } } @Override protected void onPause() { super.onPause(); if(theMap!=null){ locMan.removeUpdates(this); } } [/java] We check for the GoogleMap object before attempting any processing, as in onCreate. If the app is pausing, we stop it from requesting location updates. If the app is resuming, we start requesting the updates again. [tip] Tip: We've used the LocationManager.NETWORK_PROVIDER a few times in this series. If you are exploring localization functionality in your apps, check out the alternative getBestProvider method with which you can specify criteria for Android to choose a provider based on such factors as accuracy and speed. [/tip] Before We Finish That pretty much completes the app! However, there are many aspects of the Google Maps Android API v2 that we have not even touched on. Once you have your app running you can experiment with features such as rotation and tilting. The updated maps service displays indoor and 3D maps in certain places. The following image shows the 3D facility with the app if the user location was in Venice, Italy: This has the map type set to normal - here is another view of Venice with the hybrid map type set: Conclusion In this tutorial series we have worked through the process of integrating both Google Maps and Google Places APIs in a single Android app. We handled API key access, setting up the development environment, workspace and application to use Google Play Services. We utilized location data, showing the user location together with nearby places of interest, and displaying the data with custom UI elements. Although what we have covered in this series is fairly extensive, it really is only the beginning when it comes to building localization features into Android apps. With the release of Version 2 of the Maps API, Android apps are set to take such functions to the next level.Read More…