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