segunda-feira, 13 de fevereiro de 2012

Serviços da Google Maps API de Geocodificação

Olá, hoje vou mostrar um exemplo de como consumir o serviço do Google Maps API de geocodificação. A definição de geocodificação eu extraí do próprio site do google:

"Geocodificação é o processo de conversão de endereços (como "1600 Amphitheatre Parkway, Mountain View, CA") em coordenadas geográficas (como latitude 37.423021 e longitude -122.083739), que você pode usar para colocar marcadores ou posicionar o mapa. A Google Geocoding API fornece uma maneira direta de acessar um geocodificador por meio de uma solicitação HTTP. Além disso, o serviço permite que você realize a operação inversa (transformar coordenadas em endereços); esse processo é conhecido como "geocodificação reversa"."

Uma solitação de geocoding deve ter basicamente os seguinte formato:
http://maps.googleapis.com/maps/api/geocode/output?parameters
onde output significa o formato do retorno dos dados, que pode ser json(recomendável) ou xml.

Além disso são obrigatórios os parâmetros:
  • address — o endereço que você deseja geocodificar
    ou
    latlng — o valor textual de latitude/longitude para o qual você deseja obter o endereço mais próximo.
  • sensor — indica se a solicitação de geocodificação vem ou não de um dispositivo com sensor de localização. Esse valor deve ser true ou false.

A resposta da requisição contém dois elementos raiz:
  • status - contém metadados sobre a solicitação. Consulte os Códigos de status no site.
  • results - contém uma matriz de informações de endereços geocodificados e informações geométricas.

Eu implementei uma biblioteca bem simples que suporta os dois formatos de resposta: json e xml.

Para interpretar o resultando em json, utilizei a biblioteca lkjson, pois, já tinha utilizado a mesma em outros projetos e achei bastante simples de usar, mais existem outras opções como o SuperObject e Delphi Web Utils.

Para interpretar o formato xml optei por utilizar o Xml Data Binding do delphi, que através de um wizard cria classes tipadas a partir do arquivos xml, deixando todo o processo de transformação transparente.

Basicamente o que fiz foi o seguinte, efetuei uma requisição no serviço de geocodificação no formato xml e salvei os dados retornados em um arquivo xml. Em seguida, utilizei o Xml Data Binding para transformar esse xml em classes. Essas classes são o resultado final da solicitação, seja ela em json ou xml.

Quando a requisição é no formato xml, é utilizado o binding do XmlDocument:
class function TGoogleApiGeoXml.XmlToGeocodeResponse(const AXmlText: String): IGeocodeResponse;
var
  vXmlDocument: IXMLDocument;
begin
  vXmlDocument := TXMLDocument.Create(nil);
  vXmlDocument.LoadFromXml(UTF8Encode(AXmlText));

  Result := vXmlDocument.GetDocBinding('GeocodeResponse', TGeocodeResponse) as IGeocodeResponse;
end;

Quando a requisição é no formato json, escrevi o parser "na unha", que retorna o mesmo tipo de objeto da solicitação em xml, deixando a aplicação que consome o serviço utilizar o mesmo sem se importar se é xml ou json.
function TGoogleApiGeoJson.ConvertFromJson(const AJsonText: String): IGeocodeResponse;
var
  vJsonResponse: TlkJSONobject;
  vResults: TlkJSONbase;
begin
  Result := TGeocodeResponse.NewGeocodeResponse;

  vJsonResponse := TlkJSONobject(TlkJSON.ParseText(UTF8Encode(AJsonText)));
  try
    vResults := TlkJSONlist(TlkJSONlist(vJsonResponse.Field['results']).Field['address_components']).Items[0];

    ParseStatus(Result, vJsonResponse);
    ParseType(Result, vResults);
    ParseFormattedAddress(Result, vResults);
    ParseAddresses(Result, vResults);
    ParseGeometry(Result, vResults);
  finally
    vJsonResponse.Free;
  end;
end;

Na sistema em si, o único código que ele precisa executar é esse:
var
  vResponse: IGeocodeResponse;
begin
  vResponse := TGoogleApiGeocodeDownload.GetGeocode("http://maps.googleapis.com/maps/api/geocode/json_ou_xml?address=1600+Amphitheatre+Parkway,+Mountain+View,+CA&sensor=true_ou_false");
 
 //Lógica que utiliza a resposta da solicitação

O implementação do método GetGeocode é bastante simples também, ele faz uma requisição HTTP utilizando indy conforme abaixo
class function TGoogleApiGeocodeDownload.HttpGet(Url: String): String;
var
  lHTTP: TIdHTTP;
  lStream: TStringStream;
begin
  lHTTP := TIdHTTP.Create(nil);
  lStream := TStringStream.Create(EmptyStr);
  try
    lHTTP.Get(Url, lStream);
    
    lStream.Position := 0;

    Result := UTF8Decode(lStream.DataString);
  finally
    FreeAndNil(lHTTP);
    FreeAndNil(lStream);
  end;
end;

E de acordo com o formato dos dados faz uma chamada ao parser correspondente para retornar o objeto.
class function TGoogleApiGeocodeDownload.GetGeocode(AUrl: String): IGeocodeResponse;
var
  vDados: String;
  vFormato: TOutputFormat;
begin
  vFormato := DiscoverFormat(AUrl);

  vDados := HttpGet(AUrl);

  case vFormato  of
    Json: Result := TGoogleApiGeoJson.JsonToGeocodeResponse(vDados);
    Xml : Result := TGoogleApiGeoXml.XmlToGeocodeResponse(vDados);   
  end;
end;

Mais informações sobre limitações de uso e sobre os parâmetros utilizados na requisição HTTP podem ser encontrados aqui.

O código fonte completo com exemplo de como utilizar pode ser acessado aqui no meu bitbucket.

Um abraço e fique ligado nos próximos posts.

Nenhum comentário:

Postar um comentário