[EDITADO EM 03/04/2013: atualizado para Google Api v2]
Olá povo,
A API do Google Maps disponível para Android é muito bacana, e incorporar esse recurso na sua aplicação pode ser um bom atrativo. Em um dos projetos que trabalhei, precisava mostrar a rota entre dois pontos no mapa, e a Google disponibilizou em 27/07/12 uma API Web que retorna a rota entre duas coordenadas geográficas no formato JSON (ou XML se preferir).
Nesse arquivo, temos uma lista com as coordenadas geográficas que devem ser percorridas desde o ponto de partida até o destino. Com essas coordenadas lidas do JSON, criamos uma lista de LatLng que devem ser desenhadas no objeto GoogleMap através de uma PolyLine.
Não vou detalhar muito a configuração do Google Maps v2 no Android pois já falei
nesse post aqui. Também não vou entrar em detalhes sobre leitura de JSON pois falei
nesse post aqui. E se você tiver dúvidas sobre AsyncTask, dê uma olhada
nesse post.
Vou começar pelo código da Activity mostrado abaixo. No onCreate pegamos a instância de GoogleMap e definimos a posição inicial no mapa e um zoom padrão. Em seguida iniciamos a AsyncTask passando a GoogleMap no construtor e informando a latitude e longitude de origem e destino.
public class TesteRotaMapaActivity
extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SupportMapFragment fragment =
(SupportMapFragment)
getSupportFragmentManager()
.findFragmentById(R.id.map);
GoogleMap map = fragment.getMap();
LatLng latLng = new LatLng(-8.102739,-34.89917);
map.animateCamera(
CameraUpdateFactory.newLatLngZoom(latLng, 12.0f));
new RotaAsyncTask(this, map).execute(
// Latitude, Logintude de Origem
latLng.latitude, latLng.longitude,
// Latitude, Longitude de Destino
-23.604262,-46.676513);
}
}
A classe RotaAsyncTask é quem faz o trabalho de acessar o site do Google Maps, baixar o JSON com a informação da rota.
public class RotaAsyncTask extends
AsyncTask<Double, Void, Void>{
private ProgressDialog dialog;
private GoogleMap mapView;
private Context context;
private Route rota;
public RotaAsyncTask(Context ctx, GoogleMap mapa) {
mapView = mapa;
context = ctx;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = ProgressDialog.show(
mapView.getContext(), "Aguarde",
"Calculando rota");
}
@Override
protected Void doInBackground(Double... params) {
rota = directions(
new LatLng(params[0], params[1]),
new LatLng(params[2], params[3]));
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
PolylineOptions options = new PolylineOptions()
.width(5)
.color(Color.RED)
.visible(true);
for (LatLng latlng : rota.getPoints()) {
options.add(latlng);
}
mapView.addPolyline(options);
dialog.dismiss();
}
private Route directions(
final LatLng start, final LatLng dest) {
// Formatando a URL com a latitude e longitude
// de origem e destino.
String urlRota = String.format(Locale.US,
"http://maps.googleapis.com/maps/api/"+
"directions/json?origin=%f,%f&"+
"destination=%f,%f&" +
"sensor=true&mode=driving",
start.latitude,
start.longitude,
dest.latitude,
dest.longitude);
GoogleParser parser;
parser = new GoogleParser(urlRota);
return parser.parse();
}
}
Quem viu o post anterior pode notar que houveram algumas modificações. Delegamos o parse do JSON para a classe GoogleParser e exibimos as rotas utilizando a classe PoluLineOptions (da API v2) com ajuda da classe Route. Essas novas classes foram extraídas desse post
aqui. Graças ao comentário do Hadson Gomes.
Eu peguei essas classes e deixei apenas o necessário. Vejamos elas abaixo.
public class Route {
private final List<LatLng> points;
private String polyline;
public Route() {
points = new ArrayList<LatLng>();
}
public void addPoints(final List<LatLng> points) {
this.points.addAll(points);
}
public List<LatLng> getPoints() {
return points;
}
public void setPolyline(String polyline) {
this.polyline = polyline;
}
public String getPolyline() {
return polyline;
}
}
Note que a classe acima tem uma propriedade chamada polyline. Nesse JSON, a rota retorna algumas coordenadas "macro" digamos assim. Por isso os comentários abaixo informando que eram traçadas linhas retas no caminho. A rota com as coordenadas mas precisas estão codificadas em base64 no atributo polyline. A classe GoogleParser faz essa conversão de base64 para GeoPoint conforme abaixo.
public class GoogleParser {
protected URL feedUrl;
public GoogleParser(String feedUrl) {
try {
this.feedUrl = new URL(feedUrl);
} catch (MalformedURLException e) {
}
}
public Route parse() {
// Cria uma rota vazia
final Route route = new Route();
try {
// Obtém a String do JSON
final String result =
convertStreamToString(
feedUrl.openConnection()
.getInputStream());
// Transforma a string em JSON
JSONObject json = new JSONObject(result);
// Pega a primeira rota retornada
JSONObject jsonRoute =
json.getJSONArray("routes")
.getJSONObject(0);
JSONObject leg = jsonRoute
.getJSONArray("legs").getJSONObject(0);
// Obtém os passos do caminho
JSONArray steps = leg.getJSONArray("steps");
final int numSteps = steps.length();
/*
* Itera através dos passos, decodificando
* a polyline e adicionando à rota.
*/
JSONObject step;
for (int i = 0; i < numSteps; i++) {
// Obtém o passo corrente
step = steps.getJSONObject(i);
// Decodifica a polyline e adiciona à rota
route.addPoints(
decodePolyLine(
step.getJSONObject("polyline")
.getString("points")));
}
} catch (Exception e) {
}
return route;
}
private String convertStreamToString(
final InputStream input) {
final BufferedReader reader =
new BufferedReader(
new InputStreamReader(input));
final StringBuilder sBuf = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sBuf.append(line);
}
} catch (IOException e) {
} finally {
try {
input.close();
} catch (IOException e) {
}
}
return sBuf.toString();
}
private List<LatLng> decodePolyLine(final String poly) {
int len = poly.length();
int index = 0;
List<LatLng> decoded =
new ArrayList<LatLng>();
int lat = 0;
int lng = 0;
while (index < len) {
int b;
int shift = 0;
int result = 0;
do {
b = poly.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlat =
((result & 1) != 0 ?
~(result >> 1) :
(result >> 1));
lat += dlat;
shift = 0;
result = 0;
do {
b = poly.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlng = ((result & 1) != 0 ?
~(result >> 1) :
(result >> 1));
lng += dlng;
decoded.add(
new LatLng(
(float)(lat / 1E5),
(float)(lng / 1E5)));
}
return decoded;
}
}
Quem viu o post anterior deve lembrar que a classe RouteOverlay não existe mais. Quem faz seu papel é a classe PolyLine da nova versão da Google Maps Api.
A imagem abaixo mostra nossa aplicação em execução.
Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber