domingo, março 29, 2009

Traçar rotas a partir do Delphi com o Google Maps

 

         Continuando a partir do artigo anterior “Mapas do Google no seu aplicativo Delphi”, vamos agora avançar um pouco mais, traçar rotas entre dois pontos, e mostrar isto ao usuário.

          Esta é uma funcionalidade interessante para aplicativos como CRM ou de logística. Você pode obter informações completas sobre como utilizar as APIs do Google Maps em www.google.com/apis/maps, a maioria dos exemplos contempla Java, Javascript, PHP ou Python, porém não é dificil adaptá-los à linguagem que você utiliza, como estamos fazendo com o Delphi.

           Para usar as APIs do Google, você precisa obter uma chave única e exclusiva sua, é de graça, e lhe dá acesso à todas as funcionalidades do Google Maps. Neste exemplo não vamos utilizar esta chave, é apenas um exemplo, muito embora você possa utilizar sem ter uma chave, como você pode constatar pelo código neste artigo, é aconselhável que você obtenha esta chave, por uma questão de copyright.

          Bueno, vamos botar a mão na massa.

          Primeiro de tudo você irá precisar, para mostrar o mapa, das coordenadas do ponto a ser mostrado na tela, normalmente você mostrará na tela o ponto inicial da rota, permitindo ao usuário que acompanhe a rota a partir do ponto de origem, porém você pode mostrar o ponto final da rota, a seu critério. Por este motivo criamos uma função para retornar as coordenadas.

          As coordenadas (e mais uma série de informações sobre o endereço) são retornadas em um arquivo xml, você também pode solicitar que seja retornado em formato csv (separado por vírgulas). No caso de um arquivo xml a API do Google Maps retorna informações bem completas, incluindo até mesmo o CEP, no caso de arquivo CSV serão retornadas apenas as coordenadas.

        Neste artigo iremos trabalhar com o arquivo XML, pois se você desejar obter mais informações sobre o endereço basta incrementar o código.

        Como este artigo é direcionado para todos os desenvolvedores DELPHI, incluindo os iniciantes, vou tentar manter o código o mais simples possível, mesmo que em algumas partes pareça que há redundância ou que o código poderia ser reduzido, creio que desta forma fica fácil para todos acompanharem a metodologia, sinta-se a vontade para refatorar o código.

       Para este código você deverá declarar a unit XMLDOC na parte de interface da sua unit.

Vamos também declarar um objeto do tipo XMLDocument para trabalhar com as informações retornadas pelo Google.

vXMLDoc : TXMLDocument;

       Na inicialização da Unit, ou no evento onCreate do Form, você deverá criar o XMLDocument:

    vXMLDoc := TXMLDocument.Create(Self);

      Vamos criar a função que irá preencher as coordenadas, apenas para fins informativos mantivemos o array com 3 dimensões, na primeira iremos manter as coordenadas como recuperadas do arquivo xml, isto é apenas para fins informativos, você poderá consultar esta ocorrência do array para verificar como as coordenadas foram lidas. No seu aplicativo final de produção você pode eliminar esta ocorrência.

function getCoordenadas(Logradouro, Numero, Cidade, UF, Pais: String): String;

var

//Vamos declarar um array para trabalhar com as coordenadas:
   strCoordenadas: array[0..2] of String;
   strURL: String;
   idx, inicio, tamanho : integer; // para manipular as strings de retorno

begin

Try

  Result := ‘’;

   strURL := ‘http://maps.google.com/maps/geo?q=’ +
                  Logradouro + ‘, ‘ + Numero + ‘ – ‘ + Cidade +
                  ‘ – ‘ + UF + ‘, ‘ + Pais + ‘&output=xml’;
   // O parâmetro final da string output determina o tipo de
   //    informação retornada (csv ou xml).
   //  Agora fazemos o xmldocument ler o retorno da URL

   vXMLDoc.FileName := strURL;
   vXMLDoc.Active := True;

   // Se você quiser aprender mais sobre o retorno do Google.
   // coloque um TMemo no formulário e passe o texto retornado
  //  para este TMemo, para ver o que é retornado, como abaixo:

  Memo1.Lines.Clear;
  Memo1.Lines.Add(vXMLDoc.XML.Text);

  if not vXMLDoc.Active Then
     exit;

For idx := 0 to vXMLDoc.XML.Count -1 do
    begin
    If Pos('<coordinates>', vXMLDoc.XML[idx]) > 0 Then
       begin
       inicio := Pos('<coordinates>', vXMLDoc.XML[idx]) + 13;
       tamanho := (Length(vXMLDoc.XML[idx]) - inicio) - (Length(vXMLDoc.XML[idx]) - (Pos('</coordinates>', vXMLDoc.XML[idx])));
       strCoordenadas[0] := Copy((vXMLDoc.XML[idx]), inicio, tamanho);
       end;
    end;

If length(strCoordenadas[0]) > 0 Then
   begin
   strCoordenadas[1] := Copy(strCoordenadas[0], 1, Pos(',', strCoordenadas[0]) -1);
   strCoordenadas[2] := Copy(strCoordenadas[0], Pos(',', strCoordenadas[0])+ 1, Length(StrCoordenadas[0]));
   if Pos(',', strCoordenadas[2]) > 0 Then
      StrCoordenadas[2] := Copy(strCoordenadas[2], 1, Pos(',', strCoordenadas[2]) - 1);
   end;

// Se você colocou um Memo para verificar o que é retornado,
//      pode adicionar as coordenadas para verificar se está tudo OK.
//  Lembre-se de retirar isto no software de produção.
Memo1.Lines.Add(strCoordenadas[0]);
Memo1.Lines.Add(strCoordenadas[1]);
Memo1.Lines.Add(strCoordenadas[2]);
Result := strCoordenadas[2] + ‘,’ + strCoordenadas[1];

Finally

End;

end;

 

    Agora vamos declarar uma função para receber os endereços, as coordenadas e retornar a URL para traçar a rota. As URLs para buscar as coordenadas e para traçar as rotas, ficam mais fácil de gerenciar se você declarar na parte de interface da unit. Aqui declaramos as URLs no corpo das procedures e functions para facilitar o seu entendimento, apenas para fins didáticos.

Function Rotear( De_onde, Para_onde, coordenadas: String): String;
var
  strURL : String;
begin
   Try
       
Result := ‘’;

   // URL para traçar a rota no google
   // O parâmetro z (o último da URL) é o fator de zoom
   //  a ser aplicado ao mapa, experimente com outros
   // valores (valores maiores, mais zoom, e vice-versa).

   strURL := ‘http://maps.google.com/maps?f=d&source=s_d&saddr=’ + 
                   De_onde + ‘&daddr=’ + 
                   Para_onde + ‘&hl=pt-BR&geocode=&mra=pe&mrcr=0&ie=UTF8&ll=’ +
                   coordenadas + ‘&z=16’;

Result := strURL;

Finally

end;

end;

 

   Agora vamos usar a mesma função que usamos no outro artigo sobre mostrar mapas do google no delphi, para mostrar o mapa com a rota traçada:

     Temos uma tabela de Clientes, 5 campos desta tabela nos interessam, que contém os dados que usaremos para pesquisar no mapa.

    Logradouro = Contém o nome do logradouro do endereço, por exemplo “Rua Jesuíno Arruda”.
    Numero = Contém o número do endereço, apenas o número e não o complemento (sala, loja, etc), por exemplo: 769 
    Cidade = O nome da cidade, por exemplo: São Paulo
    UF = A sigla do estado, exemplo SP.
    País = O nome do país;

    E agora criar uma função que vai fazer o trabalho, é simples, prático e bem rápido.

procedure TForm1.CarregaMapa;
var

   strURL, deOnde, paraOnde, coordenadas : String;
begin
  // Localizamos o endereço inicial

  Clientes.Locate(‘ClienteID’, VarArrayOf([clienteInicial]),[]);

   Coordenadas := getCoordenadas(Clientes.FieldByName('Logradouro').AsString, Clientes.FieldByName('Numero').AsString,       Clientes.FieldByName('Cidade').AsString, Clientes.FieldByName('UF').AsString, Clientes.FieldByName(‘Pais’).AsString);

  deOnde := Clientes.FieldByName('Logradouro').AsString + ', ' +
                  Clientes.FieldByName('Numero').AsString + ', ' + 
                  Clientes.FieldByName('Cidade').AsString + '-' +
                  Clientes.FieldByName('UF').AsString + ‘, ‘ +
                  Clientes.FieldByName(‘Pais’).AsString;

 

// Vamos localizar o endereço de destino
  Clientes.Locate(‘ClienteID’, varArrayOf([clientefinal]), []);

        paraOnde := Clientes.FieldByName('Logradouro').AsString + ', ' +
                  Clientes.FieldByName('Numero').AsString + ', ' + 
                  Clientes.FieldByName('Cidade').AsString + '-' +
                  Clientes.FieldByName('UF').AsString + ‘, ‘ +
                  Clientes.FieldByName(‘Pais’).AsString;

  strURL := Rotear(deOnde, paraOnde, Coordenadas);
  ShellExecute(0, Nil,  PChar(strURL), Nil, Nil, 0);

// Fiz a chamada acima para que ficasse mais claro, mas você
// pode otimizar a clareza do código fazendo a chamada :
// ShellExecute(0, Nil,  PChar(Rotear(deOnde, paraOnde, Coordenadas)), Nil, Nil, 0);


end;

    Bom, com isso você já consegue colocar a funcionalidade de fazer rotas no seu aplicativo, você pode usar um TWebBrowser e embutir isto dentro do seu software. Há vantagens e desvantagens nisto, eu pessoalmente prefiro chamar um browser externo.

     Como o TWebBrowser é baseado na engine do Internet Explorer, se o usuário tiver outro browser instalado (Opera, FireFox, Safari, etc), e não tiver o Internet Explorer instalado, seu aplicativo vai apresentar erro, e a bela funcionalidade que você implementou não irá funcionar. Da maneira como fizemos, o browser padrão do usuário é que será chamado, seja ele qual for.

    www.spectrus.com.br

14 comentários:

Anônimo disse...

Como disse lá no News, ganhei o dia (e mais trabalho) mostrando isso pro chefe.

Já implementei na minha aplicação.

Valeu.
Felipe

Unknown disse...

Parabens pela dedicação em postar este material muito útil!!!

Anônimo disse...

excelente material, parabens mesmo, mas e como que eu faço para enviar as coordenadas e pegar o endereco?

AcidBytes disse...

Você fala sobre as coordenadas geográficas? Latitude e Longitude?

crisck disse...

voce tem os fontes para download?

AcidBytes disse...

O código é esse que está no post mesmo, na verdade foi extraído de um aplicativo de CRM que eu tenho.

Anônimo disse...

thanks for this exacting tips 147896325

Anônimo disse...

Gostaria de sabe se e possivel traçar mais de um destino e como seria isto.

AcidBytes disse...

Não entendi, você deseja a partir de um destino traçar mais de uma rota ao mesmo tempo? Ou oferecer ao usuário rotas alternativas?

O destino é um só para cada rota, porém automaticamente o google inclui as rotas alternativas, para isto você não precisa fazer nada, já é apresentado por default.

Anônimo disse...

E se quisermos que a rota seja traçada saindo do Endereço_1 (origem), passando pelo Endereço_2, Endereço_3 e chegando ao destino Endereço_4 (final) ?

Marcelo
jaious@hotmal.com

Walter Chagas Jr. disse...

Olá amigo, muito bom seu artigo mas o que eu preciso mesmo é um recurso de colocar no GMaps uma rota feita por um motorista baseado nas coordenadas obtidas por um GPS a bordo do veículo. Tipo uma rota de coleta de material que foi realizada. Voce teria como me dar alguma dica de como fazer isto?

Desde já, obrigado pela atenção dispensada.

Anônimo disse...

Boa tarde.

Tenho as coordenadas dos fornecedores cadastradas, latitude e longitude, entao quero que o o cliente ache o fornecedor mais perto dele, e me relacione os mais perto até x fornecedores.

Teria como se encaixar no meu caso?

Paulo
comercial@g2soft.com.br

Abraço!

Unknown disse...

fiz mas da erro no retorno linha 0 no xmldocument
teria como pegar o fonte

Edson Vasconcelos disse...

bom dia, poderia me passar os fontes para meu email edson172012@gmail.com