Android Lesson 25 - Android Mapping

Alternative 1. Using Google Maps API + WebViews + Intents


Example 1. Intent passing static URL to HTTPS call
(map is rendered as a PNG figure) /
Example 2. Intent passing static URL to HTTPS call
(determine route from starting to ending point)

Example3. Loading an HTML + JavaScript page into a WebView

packagecsu.matos;
// Google Maps JavaScript API - Android-WebView
//
// ------
// Load a local html file on a WebView. The html file contains
// a JS script method to show a Google Map V3 centered around
// UCR San Jose CR (zoom level 13/23, centered, sensor=true)
// The image on the Android device is the same that would be
// displayed on any other webBrowser (laptop, iPhone, etc.)
// ------
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
public class MainActivityextends Activity {
WebViewwebview;
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//VERSION1A. Using Google Static Maps API (See Lesson 10 - Intents)
// Create your map based on URL parameters sent through a standard HTTPS request.
// Display the map as an image.
// REFERENCE
// ------
//
// String thePlaceUrl = "
// "center=Cleveland+State+University,Ohio"+
// "&zoom=15&size=300x600&scale=2&maptype=roadmap" +
// "&markers=color:blue%7Clabel:B%7C41.5020952,-81.6789717"+
// "&markers=color:green%7Clabel:E%7C41.503500,-81.6729999" +
// "&key=AIzaSyBxCR2d8FVUKXbYmaze4vG1tWS0M2mdAO0";
// Intent intent1 = new Intent(android.content.Intent.ACTION_VIEW,
// Uri.parse(thePlaceUrl));
// startActivity(intent1);
// //VERSION1B. Using Google Static Maps API
// Intent intent2 = new Intent(
// android.content.Intent.ACTION_VIEW,
// Uri.parse("
// + "saddr=9.938083,-84.054430&"
// + "daddr=9.926392,-84.055964"));
// startActivity(intent2);
// VERSION2. Using Google Maps JavaScript API
// load into WebView an asset HTML page defining mapping operations
webview= (WebView) findViewById(R.id.webview);
webview.getSettings().setJavaScriptEnabled(true);
webview.loadUrl("file:///android_asset/google_map.html");
}//onCreate
}//class

Layout

<?xml version="1.0" encoding="utf-8"?>
LinearLayout
xmlns:android="
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout

/res/assets/google_map.html

NOTE: From main menu File > New > Android > Folder > Assets Folder – Copy HTML file in new folder

<!DOCTYPE html
html
head
meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
style type="text/css"
html { height: 100% }
body { height: 100%; margin: 0px; padding: 0px }
#map_canvas { height: 100% }
</style
script type="text/javascript"
src="
</script
script type="text/javascript"
function initialize() {
varlatlng = new google.maps.LatLng(41.5020952, -81.6789717);
varmyOptions = {
zoom: 15,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
var marker = new google.maps.Marker({
position: latlng,
map: map,
title: 'Engaged Education'
});
}
</script
</head
body onload="initialize()"
<div id="map_canvas" style="width:100%; height:100%"</div
</body
</html

Manifest

uses-permission android:name="android.permission.INTERNET" />

ALTERNATIVE 2. Using Android’s MapFragment Class

Example1. Draw Map-Markers on top of Cleveland (CSU) and San Jose CR (UCR).

package csu.matos;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.UiSettings;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
public class MapsActivityextends FragmentActivityimplements OnMapReadyCallback {
private GoogleMapmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragmentmapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
/**
* Manipulates the map once available.
* This callback is triggered when the map is ready to be used.
* This is where we can add markers or lines, add listeners or move the camera. In this case,
* we just add a marker near Sydney, Australia.
* If Google Play services is not installed on the device, the user will be prompted to install
* it inside the SupportMapFragment. This method will only be triggered once the user has
* installed Google Play services and returned to the app.
*/
@Override
public void onMapReady(GoogleMapgoogleMap) {
map = googleMap;
// Add a marker in Sydney and move the camera
// LatLngsydney = new LatLng(-34, 151);
// mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
// mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
// ------
// MAP_TYPES: HYBRID, NONE, NORMAL, SATELLITE, TERRAIN
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
// disable indoor maps
map.setIndoorEnabled(false);
// this point represents location of Cleveland State University
LatLng CSU_OHIO = new LatLng(41.501936, -81.675278);
Marker csu_ohio_marker = map.addMarker(new MarkerOptions()
.position(CSU_OHIO)
.title("Cleveland State University")
.snippet("Cleveland, Ohio") );
map.moveCamera(CameraUpdateFactory.newLatLngZoom(CSU_OHIO, 15.0f));
// set up map UI settings:
UiSettingsmapUI = map.getUiSettings();
// enable: pan, zoom, tilt, rotate
mapUI.setAllGesturesEnabled(true);
// enable compass
mapUI.setCompassEnabled(true);
// enable zoom controls
mapUI.setZoomControlsEnabled(true);

// this marker represents Universidad de Costa Rica
LatLng SANJOSE1_CR = new LatLng(9.937931, -84.051936);
Marker san_jose1_marker = map.addMarker(new MarkerOptions()
.position(SANJOSE1_CR)
.title("Universidad de Costa Rica")
.snippet("San Jose, CR")
.icon(BitmapDescriptorFactory.defaultMarker(
BitmapDescriptorFactory.HUE_GREEN)) );
// drawing a straight line between the two points
Polyline line = map.addPolyline(new PolylineOptions()
.add( SANJOSE1_CR, CSU_OHIO )
.width(10)
.color(Color.BLUE));
// this point is halfway between Cleveland and San Jose
LatLnghalfWay = new LatLng( (SANJOSE1_CR.latitude + CSU_OHIO.latitude)/2,
(SANJOSE1_CR.longitude + CSU_OHIO.longitude)/2 );
Marker halfWay_marker = map.addMarker(new MarkerOptions()
.position(halfWay)
.title("Mid-Point")
.icon(BitmapDescriptorFactory.defaultMarker(
BitmapDescriptorFactory.HUE_YELLOW)) );
map.moveCamera(CameraUpdateFactory.newLatLngZoom(halfWay, 4.0f));
}
}

Demo1 – Layout

fragment android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
xmlns:android="
xmlns:map="
xmlns:tools="
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="csu.matos.MapsActivity"/>

Manifest

<?xml version="1.0" encoding="utf-8"?>
manifest package="csu.matos"
xmlns:android="
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
location permissions for the 'MyLocation' functionality.
-->
uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/google_maps_api.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_maps_key"/>
<activity
android:name=".MapsActivity"
android:label="@string/title_activity_maps"
<intent-filter
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter
</activity
</application
</manifest

google_maps_api.xml

resources
<!--
TODO: Before you run your application, you need a Google Maps API key.
To get one, follow this link, follow the directions and press "Create" at the end:

You can also add your credentials to an existing key, using this line:
1A:5A:D8:1E:7D:5F:88:AD:47:23:5F:89:6D:9D:7F:36:AA:52:EF:DF;csu.matos
Alternatively, follow the directions here:

Once you have your key (it starts with "AIza"), replace the "google_maps_key"
string in this file.
-->
string name="google_maps_key" templateMergeStrategy="preserve" translatable="false"
AIzaSyBnBU1FxEiw7zJ5p9oubtYrReEsSPYjnVs
</string
</resources


Demo2 – GeoCoding – From a ‘vague’ landmark to all possible matches (XAL fomat – Extended Address Language)

GEOCODING - This app invokes the method:
public List<Address> getFromLocationName (String locationName, intmaxResults)
Returns an array of Addresses that are known to describe the named location,which may be a place name such as "Dalvik, Iceland", an address such as "1600 Amphitheatre Parkway, Mountain View, CA", an airport code such as "SFO", etc..
The returned addresses will be localized for the locale provided to this class's constructor.The query will block and returned values will be obtained by means of a network lookup.
(Taken from Android-API-22 Documentation) /

public class MapsActivityextends Activity {
// GoogleMap object used for drawing the map and handling user interactions
private GoogleMapmap;
private EditTexttxtAddress;
Button btnSearch;
private List<Address> resultingAddresses= null;
String txtLandmarkLocation= "";
private Handler mainHandler= new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// hopefully we get here a list of addresses from asynctask
resultingAddresses= (List<Address>) msg.obj;
// transfer resulting addresses to array: items
intn = resultingAddresses.size();
String[] items = new String[ n ];
//transfer data to items[] array
for (inti=0; i<n; i++ ){
items[i] = "\nOPTION-" + i + "\n" + resultingAddresses.get(i).toString();
}
// show items[] in a dialog box
AlertDialog.Builder builder = new AlertDialog.Builder(MapsActivity.this);
builder.setTitle("Make your selection");
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, intitem) {
showSelectedMap(resultingAddresses.get( item) );
}
});
AlertDialog alert = builder.create();
alert.show();
}
};
private void showSelectedMap(Address address) {
String text = "";
LatLngcoord = new LatLng(address.getLatitude(), address.getLongitude() );
// combine all available address-lines of selected item into string: text
for (inti=0; iaddress.getMaxAddressLineIndex(); i++ ){
text += address.getAddressLine(i) + " ";
}
text += " Lat: " + address.getLatitude();
text += " Lng: " + address.getLongitude();
txtAddress.setText( txtLandmarkLocation);
Marker coordMarker = map.addMarker(new MarkerOptions()
.position(coord)
.title(text) );
map.moveCamera(CameraUpdateFactory
.newLatLngZoom(coord, 15.0f));
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
txtAddress= (EditText) findViewById(R.id.txtAddress);
txtAddress.setText( "Main ave. Ohio" );
btnSearch= (Button) findViewById(R.id.btnSearch);
btnSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
txtLandmarkLocation= txtAddress.getText().toString();
AsyncGetAddressListasynctask = new AsyncGetAddressList();
asynctask.execute( txtLandmarkLocation);
}
});
setupMap();
}// onCreate
private void setupMap() {
// draw a map centered on [0,0] coordinates
map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
// set up map UI settings:
// - enable all gestures - pan, zoom, tilt, rotate
// - enable compass
// - enable zoom controls
UiSettingsmapUI = map.getUiSettings();
mapUI.setAllGesturesEnabled(true);
mapUI.setCompassEnabled(true);
mapUI.setZoomControlsEnabled(true);
}
@Override
public booleanonOptionsItemSelected(MenuItem item) {
String text = GooglePlayServicesUtil
.getOpenSourceSoftwareLicenseInfo(this);
new AlertDialog.Builder(this)
.setTitle("About Google Maps")
.setMessage(text)
.setNeutralButton("Cancel",null)
.show();
return true;
}
// AsyncTaskParams, Progress, Result>
public class AsyncGetAddressListextends AsyncTask<String, Long, List<Address> {
ProgressDialogdialog;
List<Address> lstFoundAddresses= null;
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(MapsActivity.this);
dialog.setTitle("Getting Locations ...");
dialog.show();
}
@Override
protected List<Address> doInBackground(String... params) {
String inputAddress = params[0]; // Get input text
inttimes = 0;
Geocoder geoC = new Geocoder(MapsActivity.this);
try {
lstFoundAddresses= geoC.getFromLocationName(inputAddress, 5);
Log.e("Goecoder>", "Total addresses found: " + lstFoundAddresses.size());
} catch (Exception e) {
Log.e("Goecoder>", "ERROR " + e.getMessage());
}
dialog.dismiss();
// pass this data to main UI thread
Message msg = mainHandler.obtainMessage(1, (List<Address>)lstFoundAddresses);
mainHandler.sendMessage(msg);
return lstFoundAddresses;
}// doInBackground
@Override
protected void onPostExecute(List<Address> result) {
super.onPostExecute(result);
// update Main UI list of addresses
resultingAddresses= result;
if (resultingAddresses.size() > 0)
txtAddress.setText(result.get(0).toString());
else
txtAddress.setText("No results...");
}
}// AsyncTask
}// Activity

Demo 3 – Reverse Geocoding

A coordinate value is used to find postal-addresses around given location

Demo3 – MapsActivity.java - Reverse Geocoding – From coordinates to a postal address

/*
------
geoCode.getFromLocation(latitude, longitude, maxMatches)
------
Return an array of Addresses that are known to describe
the area immediately surrounding the given latitude and longitude.
The returned addresses will be localized for the locale provided
to this class's constructor.
The returned values may be obtained by means of a network lookup.
The results are a best guess and are not guaranteed to be meaningful
or correct (Android Developer's Documentation).
*/
package csu.matos;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.location.Address;
import android.location.Geocoder;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.UiSettings;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import java.util.List;
import java.util.Locale;
public class MapsActivityextends Activity {
// Approximate coordinates of the College of Business at Cleveland State University
double latitude = 41.5020952;
double longitude = -81.6789717;
// GoogleMap object used for drawing the map and handling user interactions
private GoogleMapmap;
// UI controls
private EditTexttxtLatitude;
private EditTexttxtLongitude;
private TextViewtxtAddress;
Button btnSearch;
private Geocoder geoC;
private List<Address> resultingAddresses= null;
private Context myContext= this;
// wait for thread to produce Address-list based on coordinates and process results here
private Handler mainHandler= new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// process Address-list from AsyncTask here
resultingAddresses= (List<Address>) msg.obj;
intn = resultingAddresses.size();
String[] items = new String[ n ];
for (inti=0; i<n; i++ ){
items[i] = "\nOption-" + i + "\n" + resultingAddresses.get(i).toString();
}
// show returned postal-address-options inside a dialog box
AlertDialog.Builder builder = new AlertDialog.Builder(MapsActivity.this);
builder.setTitle("Make your selection");
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, intposition) {
// Do something with the selection
Address selectedAddress = resultingAddresses.get(position);
showSelectedMap(selectedAddress);
}
});
AlertDialog alert = builder.create();
alert.show();
}
};
private void showSelectedMap(Address address) {
String selectedMsg = "";
LatLngcoord = new LatLng(address.getLatitude(), address.getLongitude() );
for (inti=0; iaddress.getMaxAddressLineIndex(); i++ ){
selectedMsg += address.getAddressLine(i) + " ";
}
selectedMsg += "\nLat: " + address.getLatitude();
selectedMsg += "\nLon: " + address.getLongitude();
txtAddress.setText(selectedMsg);
setupMap();
Marker coordMarker = map.addMarker(new MarkerOptions()
.position(coord)
.title(selectedMsg) );
map.moveCamera(CameraUpdateFactory
.newLatLngZoom(coord, 15.0f));
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
txtLatitude= (EditText) findViewById(R.id.txtLatitude);
txtLongitude= (EditText) findViewById(R.id.txtLongitude);
txtAddress= (TextView) findViewById(R.id.txtAddress);
txtAddress.setText("Approximated Postal Address will be shown here...");
txtLatitude.setText("41.5020952");
txtLongitude.setText("-81.6789717");

btnSearch= (Button) findViewById(R.id.btnSearch);
btnSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AsyncGetAddressListmapAsynctask = new AsyncGetAddressList();
mapAsynctask.execute(txtLatitude.getText().toString(),
txtLongitude.getText().toString());
}
});
// setupMap();
}// onCreate
private void setupMap() {
// draw a map centered on [0,0] coordinates
map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
// set up map UI settings:
// - enable all gestures - pan, zoom, tilt, rotate
// - enable compass
// - enable zoom controls
UiSettingsmapUI = map.getUiSettings();
mapUI.setAllGesturesEnabled(true);
mapUI.setCompassEnabled(true);
mapUI.setZoomControlsEnabled(true);
}
@Override
public booleanonOptionsItemSelected(MenuItem item) {
String text = GooglePlayServicesUtil
.getOpenSourceSoftwareLicenseInfo(this);
new AlertDialog.Builder(this)
.setTitle("About Google Maps")
.setMessage(text)
.setNeutralButton("Cancel", null)
.show();
return true;
}
// AsyncTaskParams, Progress, Result>
public class AsyncGetAddressListextends AsyncTask<String, Long, List<Address> {
ProgressDialogdialog;
List<Address> lstFoundAddresses= null;
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(MapsActivity.this);
dialog.setTitle("Getting Locations ...");
dialog.show();
}

@Override
protected List<Address> doInBackground(String... params) {
double latitude = Double.valueOf(params[0]);
double longitude = Double.valueOf(params[1]);
Geocoder geoC = new Geocoder(MapsActivity.this, Locale.US);
try {
lstFoundAddresses= geoC.getFromLocation(latitude, longitude, 5);
Log.e("Goecoder>", "Total addresses found: " + lstFoundAddresses.size());
} catch (Exception e) {
Log.e("Goecoder>", "ERROR " + e.getMessage());
}
dialog.dismiss();
// pass this data to main UI thread
Message msg = mainHandler.obtainMessage(111, (List<Address>)lstFoundAddresses);
mainHandler.sendMessage(msg);
return lstFoundAddresses;
}// doInBackground
@Override
protected void onPostExecute(List<Address> result) {
super.onPostExecute(result);
}
}// AsyncTask
}// Activity

Demo3 – Layout

<?xml version="1.0" encoding="utf-8"?>
LinearLayoutxmlns:android="
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="Reverse GeoCoding [Coordinates to Address] "/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#ffaaaaaa"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="Latitude: "/>
<EditText
android:id="@+id/txtLatitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_weight="2"
android:hint="enter latitude (+-decimal)"
android:inputType="numberSigned|numberDecimal"
android:textSize="12sp"/>
</LinearLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="Longitude: "/>
<EditText
android:id="@+id/txtLongitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_weight="2"
android:hint="enter longitude (+-decimal)"
android:inputType="numberSigned|numberDecimal"
android:textSize="12sp"/>
</LinearLayout
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#ffaaaaaa"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
<TextView
android:id="@+id/txtAddress"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_margin="1dp"
android:layout_weight="2"
android:background="#ffeeeeff"
android:hint="Approx. postal address ..."
android:lines="5"
android:textSize="12sp"/>
<Button
android:id="@+id/btnSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="1dp"
android:text="Search"/>
</LinearLayout
<fragment
android:id="@+id/map"
android:name="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout