Por que eu devo ler este artigo:

Este artigo apresenta como podemos manipular geocódigos para transformá-los em endereços. Esse tipo de procedimento é extremamente útil ao desenvolvermos aplicações que façam uso de informações de localização geográfica para fornecer algum tipo de serviço para o usuário. Muitas vezes, informações numéricas de latitude e longitude são difíceis de serem interpretadas, desta forma, nada melhor do que apresentá-las como endereços. Por outro lado, para armazenamento ou transferência de dados, ainda é melhor trabalhar com os dados numéricos, uma vez que os dados textuais costumam ocupar mais espaço.

Uma técnica contemporânea para o Geolocalização é a utilização do GPS (Geographic Position System). Esse recurso, largamente difundido, é encontrado em rastreadores de automóveis, aparelhos de orientação de direção e rotas e atualmente na maioria dos smartphones e tablets. A popularização dessa tecnologia e a possibilidade do desenvolvimento de aplicativos que utilizam o GPS do dispositivo tornou possível o desenvolvimento de inúmeras soluções em geolocalização.

Ao utilizar um GPS, as coordenadas retornadas são longitude, latitude e altitude. A última informação é retornada em metros, já as outras são retornadas em graus, minutos e segundos. Nos sistemas computacionais, essas informações se dão no formato decimal, tornando-as mais fáceis de serem tratadas, mas não mais fáceis de serem interpretadas pelos usuários.

Ao recuperar uma determinada posição, os números obtidos não dizem muita coisa. Muitas vezes, para um geocódigo (como são chamadas as coordenadas retornadas pelo GPS) ser entendido, ele precisa ser transportado para um mapa para só então o usuário conseguir interpretá-lo. Outra opção é transformar geocódigos em notações textuais, permitindo que o usuário, em vez de visualizar o geocódigo, visualize o endereço formado por nome da rua, número, bairro, cidade e estado.

Com o objetivo de facilitar o uso de GPS em aplicações Android, este artigo apresenta a Geocoding API.

Google Geocoding API

A API Google Geocodig é uma forma de utilizar um geocodificador através de um serviço HTTP. A geocodificação consiste em transformar endereços em coordenadas, como Via do Conhecimento, km1, Pato Branco, Paraná em -26.196223 e -52.689523. Também é possível realizar a operação inversa, transformando coordenadas em endereços.

Esse serviço é utilizado normalmente para geocodificar endereços previamente conhecidos para que torne possível localizá-los em um mapa. Essa é uma rotina custosa e demorada, por isso o ideal é que, sempre que possível, o desenvolvedor persista em banco de dados ou em cache os geocódigos de endereços estáticos. Isso tonará seus aplicativos mais rápidos uma vez que melhorará o tempo de resposta da sua aplicação, pois as consultas seriam realizadas com a API apenas quando necessário. Esse serviço da Google está limitado a 2.500 consultas ao dia e quem possui o pacote de serviços empresarial da Google pode realizar até 100.000 consultas por dia.

O parâmetro output deve definir o formato da resposta da solicitação, sendo possível informar json ou xml. A recomendação da própria Google é a utilização do formato JSON (notação de objetos JavaScript), já que o tamanho da sua mensagem de resposta (em bytes, por exemplo) é menor do que o tamanho da mensagem do XML.

Existe a possibilidade de utilizar o protocolo HTTP ou HTTPS. O segundo é recomendado quando os parâmetros informados são confidenciais uma vez que o protocolo HTTPS provê a proteção das informações passadas por parâmetro. Independente do protocolo escolhido, o formato dos parâmetros de envio ou o conteúdo da resposta será o mesmo.

Os parâmetros devem seguir os padrões de URLs, ou seja, cada parâmetro deve ser separado pelo caractere “&”.

Formato JSON

O formato JSON é uma notação leve de troca de dados, sendo considerada fácil de ler e escrever. O resultado de uma requisição JSON é completo. Para exemplificar, a Listagem 1 apresenta parte de uma mensagem retornada pelo Google Geocoding API. Observem que os principais dados, em especial aqueles que se referem ao endereço, se encontram no corpo da mensagem.


{
 "results" : [
 {
   "address_components" : [
   {
     "long_name" : "333-343",
     "short_name" : "333-343",
     "types" : [ "street_number" ]
   },
   {
     "long_name" : "Avenida Elisa Rosa Cola Padoan",
     "short_name" : "Av. Elisa Rosa Cola Padoan",
     "types" : [ "route" ]
   },
   {
   "long_name" : "Fraron",
   "short_name" : "Fraron",
   "types" : [ "sublocality_level_1", "sublocality", "political" ]
   },
   {
   "long_name" : "Pato Branco",
   "short_name" : "Pato Branco",
   "types" : [ "locality", "political" ]
   },
   {
   "long_name" : "Pato Branco",
   "short_name" : "Pato Branco",
   "types" : [ "administrative_area_level_2", "political" ]
   },
   {
   "long_name" : "Paraná",
   "short_name" : "PR",
   "types" : [ "administrative_area_level_1", "political" ]
   },
   {
   "long_name" : "Brasil",
   "short_name" : "BR",
   "types" : [ "country", "political" ]
   },
   {
   "long_name" : "85508",
   "short_name" : "85508",
   "types" : [ "postal_code_prefix", "postal_code" ]
   }
   ],
   "formatted_address" : "Av. Elisa Rosa Cola Padoan, 
           333-343 - Fraron, Pato Branco - PR, Brasil",
   "geometry" : {
     "bounds" : {
     "northeast" : {
       "lat" : -26.1965843,
       "lng" : -52.6890572
     },
   "southwest" : {
     "lat" : -26.1966978,
     "lng" : -52.6892667
   }
   }
      ... 
 }
],
 "status" : "OK"
}
Listagem 1. Código JSON em resposta a uma requisição para transformar geocódigo em endereço pela Google Geocoding API

A resposta JSON possui dois elementos básicos: O primeiro é o results, que contém uma matriz de informações de endereços geocodificados e informações de geometria, e o segundo é o status, que contém metadados sobre a solicitação.

Existem outros resultados possíveis. ZERO_RESULTS indica que nenhum resultado foi retornado e normalmente acontece porque o endereço não foi encontrado. Outro exemplo é o OVER_QUERY_LIMIT, que indica que a cota diária de consultas foi ultrapassada. Um exemplo de erro ZERO_RESULTS é apresentado na Figura 1.

Tela de um browser após uma pesquisa sem resultado
Figura 1. Tela de um browser após uma pesquisa sem resultado

Formato XML

O XML é uma linguagem de marcação largamente utilizada nas tecnologias computacionais e na comunicação com serviços web. É recomendado para documentos com dados com organizações hierárquicas, sendo um dos dois tipos de retorno possíveis nessa API. O resultado da mesma consulta anterior feita com retorno XML é apresentado na Listagem 2.


<GeocodeResponse>
<status>OK</status>
<result>
 <type>street_address</type>
 <formatted_address>
   Av. Elisa Rosa Cola Padoan, 333-343 - Fraron, Pato Branco - PR, Brazil
 </formatted_address>
 <address_component>
   <long_name>333-343</long_name>
   <short_name>333-343</short_name>
   <type>street_number</type>
 </address_component>
 <address_component>
   <long_name>Avenida Elisa Rosa Cola Padoan</long_name>
   <short_name>Av. Elisa Rosa Cola Padoan</short_name>
   <type>route</type>
      </address_component>
      <address_component>
          <long_name>Fraron</long_name>
          <short_name>Fraron</short_name>
          <type>sublocality_level_1</type>
          <type>sublocality</type>
          <type>political</type>
      </address_component>
      <address_component>
          <long_name>Pato Branco</long_name>
          <short_name>Pato Branco</short_name>
          <type>locality</type>
          <type>political</type>
      </address_component>
      <address_component>
          <long_name>Pato Branco</long_name>
          <short_name>Pato Branco</short_name>
          <type>administrative_area_level_2</type>
          <type>political</type>
      </address_component>
      <address_component>
          <long_name>Paraná</long_name>
          <short_name>PR</short_name>
          <type>administrative_area_level_1</type>
          <type>political</type>
      </address_component>
      <address_component>
          <long_name>Brazil</long_name>
          <short_name>BR</short_name>
          <type>country</type>
          <type>political</type>
      </address_component>
      <address_component>
          <long_name>85508</long_name>
          <short_name>85508</short_name>
          <type>postal_code_prefix</type>
          <type>postal_code</type>
      </address_component>
      <geometry>
          <location>
              <lat>-26.1966585</lat>
              <lng>-52.6892299</lng>
          </location>
          <location_type>RANGE_INTERPOLATED</location_type>
          <viewport>
           <southwest>
             <lat>-26.1979900</lat>
             <lng>-52.6905109</lng>
         </southwest>
         <northeast>
           <lat>-26.1952921</lat>
           <lng>-52.6878130</lng>
             </northeast>
         </viewport>
Listagem 2. Código XML em resposta a uma requisição para transformar geocódigo em endereço pela Google Geocoding API

A resposta XML possui um elemento raiz chamado GeocodeResponse e, assim como o JSON, dois elementos principais de nível superior: status e result.

Parâmetros para consulta

Para acesso ao serviço, um conjunto de parâmetros deve ser informado. A partir de agora serão apresentados os parâmetros obrigatórios para consulta. Na sequência teremos os não obrigatórios e, por fim, os parâmetros usados no pacote de Serviço Empresarial.

Address, latlng e components

O parâmetro address deve ser utilizado para indicar o endereço que se deseja codificar. Veja um exemplo de seu uso a seguir:

http://maps.googleapis.com/maps/api/geocode
/json?address=Via+do+conhecimento,km1,+Pato+Branco,PR

Já para pesquisas utilizando a longitude e a latitude, o parâmetro latlng deve ser utilizado da seguinte forma:

http://maps.googleapis.com/maps/api/geocode
/xml?latlng=-26.196223,-52.689523

Adicionando o parâmetro components é possível filtrar os resultados por uma região específica. Como o componente é o mesmo para o mundo todo, é interessante filtrar ao menos o país onde se deseja realizar a pesquisa.

Um filtro consiste em uma lista contendo componente:valor separado com uma barra vertical, o caractere |. Desta forma, apenas os valores que estiverem dentro dos filtros especificados serão retornados pelo WebService:

http://maps.google.com/maps/api/geocode/json?address=Via+do+conhecimento,km1,
+Pato+Branco,PR &components=country:BR

No exemplo anterior foi adicionado no parâmetro o componente country, que indica a restrição de país. Esse é o nome de um determinado país ou então a abreviação de duas letras definida no padrão ISO 3166-1.

Language

Determina o idioma que os resultados devem ser retornados. Caso o parâmetro não seja informado, a API tentará retornar o idioma nativo referente ao local do qual a solicitação foi enviada.

Region

Esse parâmetro fornece resultados de um domínio específico. Por exemplo, se é realizada uma consulta utilizando o domínio do Brasil (region=BR), o resultado da consulta trará um nível de detalhamento maior (referente ao endereço) caso seja realizada uma pesquisa com geocódigos que se encontram dentro do território brasileiro. Entretanto, se realizada uma consulta com esse mesmo domínio e com um geocódigo pertencendo à Argentina, não será recuperado um grau de detalhamento tão grande dado que o geocódigo não pertence ao domínio informado.

http://maps.google.com/maps/api/geocode
/json?components=administrative_area:parana|country:Brazil®ion=BR

Bounds

É possível também fazer com que o serviço favoreça resultados que se encontrem em uma determinada janela de visualização. Da mesma forma que o parâmetro region, o bounds apenas favorece os resultados, ele não impede que outros resultados relevantes sejam retornados.

Esse parâmetro define coordenadas de latitude e longitude dos cantos sudoeste e nordeste da janela delimitadora, com a barra vertical | separando os valores, conforme exemplificado a seguir:

http://maps.google.com/maps/api/geocode
/json?components=administrative_area:parana|country:Brazil&
bounds=-26.7172983,-54.6192979|-22.5166644,-48.0235303

Parâmetros para uso em Serviço Empresarial

Para os usuários que possuírem serviço da API para empresas, devem ser incluídos mais dois parâmetros válidos em sua solicitação de geolocalização: client e signature. O parâmetro client é o ID do cliente e o signature é uma assinatura exclusiva composta por uma chave criptográfica válida.

Estudo de caso

Utilizaremos um aplicativo que usa o GPS de um device Android para recuperar as posições de latitude e longitude. Essas informações serão apresentadas na tela do device em componentes TextView. Na tela, também haverá um botão que, ao ser pressionado, acessará o serviço de Geocoding API, convertendo o geocódigo para dado textual e apresentando-o para o usuário.

Vamos iniciar um novo projeto Android chamado AutoLocalizacao. Esse projeto terá uma única tela, assim, teremos o arquivo activity_principal.xml e PrincipalActivity.java. O código fonte referente à tela principal é apresentado na Listagem 3.


<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  tools:context=".PrincipalActivity">

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Latitude:" />

  <TextView
      android:id="@+id/tvLatitude"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textAppearance="?android:attr/textAppearanceLarge"
      android:text="0.0" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Longitude"/>


  <TextView
      android:id="@+id/tvLongitude"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textAppearance="?android:attr/textAppearanceLarge"
      android:text="0.0" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Descrição:" />

  <TextView
      android:id="@+id/tvDescricao"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textAppearance="?android:attr/textAppearanceLarge"
      android:text="" />

  <Button
      android:id="@+id/btBuscarDescricao"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Buscar Descrição Local"
      android:onClick="btBuscarDescricaoOnClick" />

</LinearLayout>
Listagem 3. activity_principal.xml – Tela principal do aplicativo

Essa tela é formada por um gerenciador de layout LinearLayout (linha 01), organizado no formato vertical (linha 5). Os componentes estão dispostos aos pares, sendo dois deles para apresentar os rótulos (linhas 08 e 20) e dois para apresentar as informações obtidas pelo GPS referentes à latitude e longitude (linhas 13 e 26). Temos também o campo descrição, que apresentará os dados retornados pelo Geocoding API (linha 38) e seu rótulo, presente na linha 33. Por fim, declaramos o botão que fará acesso à API da Google (linha 45).

Observem que todos os componentes visuais de entrada, processamento e saída de dados possuem um nome informado na tag android:id. O botão btBuscarDescricao possui o método btBuscarDescricaoOnClick() associado ao seu evento de click, método esse que deve ser codificado na classe Java. O passo seguinte é a codificação dessa classe, conforme apresentado na Listagem 4.


package br.edu.utfpr.localizacao;

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLEncoder;


public class PrincipalActivity extends Activity implements LocationListener {

  private TextView tvLongitude;
  private TextView tvLatitude;
  private TextView tvDescricao;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_principal);

      tvLongitude = (TextView) findViewById( R.id.tvLongitude );
      tvLatitude = (TextView) findViewById( R.id.tvLatitude );
      tvDescricao = (TextView) findViewById( R.id.tvDescricao );

      LocationManager lm = (LocationManager)
                  this.getSystemService( Context.LOCATION_SERVICE );

      lm.requestLocationUpdates( LocationManager.GPS_PROVIDER, 2000, 0, this );

  }

  @Override
  public void onLocationChanged(Location location) {
      double latitude = location.getLatitude();
      double longitude = location.getLongitude();

      tvLatitude.setText( String.valueOf( latitude ) );
      tvLongitude.setText( String.valueOf( longitude ) );
  }

  @Override
  public void onStatusChanged(String provider, int status, Bundle extras) {

  }

  @Override
  public void onProviderEnabled(String provider) {

  }

  @Override
  public void onProviderDisabled(String provider) {

  }

  public void btBuscarDescricaoOnClick( View v ) {
      new Conexao().execute( tvLatitude.getText().toString(),
              tvLongitude.getText().toString() );
  }

  class Conexao extends AsyncTask<String, String, String> {

   @Override
   protected String doInBackground(String... params) {
     try {
       String url = "https://maps.googleapis.com/maps/api/geocode/xml?latlng="
       + params[0] + "," + params[1];
                      
       HttpClient httpclient = new DefaultHttpClient();
       HttpGet request = new HttpGet(url);
       InputStream in = httpclient.execute(request).getEntity()
       .getContent();

       BufferedReader br = null;
       StringBuilder sb = new StringBuilder();

       br = new BufferedReader(new InputStreamReader(in));

       String line = br.readLine();

       while (line != null) {
         sb.append(line);
         line = br.readLine();

     }

     return sb.toString();

    } catch ( Exception e ) {
      return "Erro: " + e.getMessage();
    }
 }

@Override
protected void onPostExecute(String msg) {
tvDescricao.setText( msg );
}

}
}
Listagem 4. PrincipalActivity.java – Classe Java para processamento da tela principal

A classe principal utiliza as primeiras linhas para declaração do pacote e importação das classes utilizadas ao longo do programa (linha 01 a 20). Na linha 23 é declarada a classe principal, assim como implementada a interface LocationListener necessária para uso do GPS.

Da linha 25 a 27 são declarados os componentes visuais do aplicativo, sendo eles recuperados nas linhas 34 a 36. Na linha 38 é recuperado o serviço de GPS e, já na linha 40, é criado um listener para rastrear mudanças de posição do device Android a cada dois segundos.

O método onLocationChanged(linha 45) é chamado a cada nova posição do GPS, recuperando a latitude e a longitude (linhas 46 e 47) e apresentando esses dados na tela do device (linhas 49 e 50).

O método btBuscarDescricaoOnClick (linha 68) trata o clique do botão, o qual inicia uma AsyncTask passando por parâmetro os campos latitude e longitude (linha 69 e 70), recuperados a partir do GPS.

Na classe interna Conexao (linha 73), no método doInBackground (linha 76), é definida uma url para utilizar a API de Geocoding. Para acesso, é utilizado um objeto HttpClient (linha 81), assim como o protocolo GET para o acesso (linha 82). Para recuperar o retorno, um objeto InputStream é instanciado (linha 83). Como o retorno é um InputStream, temos que convertê-lo para String. Para isso, é utilizado um StringBuilder (linha 87) e um BufferedReader (linha 86), o qual recebe o InputStreamReader na linha 89. Cada linha é adicionada ao objeto sb (instância do StringBuilder) a fim de criar a String completa com o resultado da chamada à Geocoding API. Ao término, essa String é retornada (linha 99).

A apresentação do resultado na tela acontece no método onPostExecute o qual recupera a resposta processada no método doInBackground() e apresenta-a na tela (linhas 107 e 108). O resultado desse processamento é apresentado na Figura 2.

Resultado do aplicativo em execução
Figura 2. Resultado do aplicativo em execução

Verifiquem que o resultado veio no formato XML devido ao parâmetro utilizado na chamada do serviço (linha 78). Dessa forma, o resultado na íntegra, sem formatação, é apresentado na tela.

Poderíamos utilizar APIs específicas para a formatação da resposta, mas como o objetivo é simplesmente recuperar o conteúdo da tag formatted_addres, utilizaremos o método substring, conforme apresentado na Listagem 5.


protected String doInBackground(String... params) {
 try {
   String url = "https://maps.googleapis.com/maps/api/geocode/xml?latlng="
   + params[0] + "," + params[1]
   + "&sensor=false";
   HttpClient httpclient = new DefaultHttpClient();
   HttpGet request = new HttpGet(url);
   InputStream in = httpclient.execute(request).getEntity()
   .getContent();

   BufferedReader br = null;
   StringBuilder sb = new StringBuilder();

   br = new BufferedReader(new InputStreamReader(in));

   String line = br.readLine();

   while (line != null) {
   sb.append(line);
   line = br.readLine();

   }

   String resposta = sb.toString();

   return resposta.substring(
     resposta.indexOf( "<formatted_address>" ) + 19,
     resposta.indexOf( "</formatted_address>" )  );

   } catch ( Exception e ) {
   return "Erro: " + e.getMessage();
   }
}
Listagem 5. Método doInBackground() – Processamento de uma informação específica

Esse código é modificado apenas das linhas 24 a 29, onde o resultado da consulta, um texto XML, é processado, recuperando apenas o conteúdo da tag <formatted_address>, o qual possui o dado textual da posição recuperada. Esse dado é apresentado na tela no componente tvDescricao. Após a execução, o resultado do programa é apresentado na Figura 3.

Resultado do aplicativo em execução com a resposta formatada
Figura 3. Resultado do aplicativo em execução com a resposta formatada

Este artigo apresentou o uso do Google Geocoding API, o qual permite conversões de geocódigos (latitude e longitude) em endereço e vice-versa. O uso dessa API traz inúmeras vantagens ao desenvolvedor, em especial para apresentação de dados geográficos na tela. Muitas vezes, informações numéricas de latitude e longitude não dizem muita coisa para o usuário, dessa forma, nada melhor do que apresentar uma informação textual referente ao lugar.

Entretanto, para armazenamento ou transferência de dados, ainda é melhor trabalhar com os dados numéricos referentes à longitude e latitude, uma vez que os dados textuais podem não ser muito precisos e costumam ocupar muito espaço se comparado aos dados numéricos.