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

Mapas do Google no seu aplicativo Delphi.

 

      Bom, você certamente já pesquisou algum endereço pelo google maps, e ficou imaginando que colocar aquilo no seu aplicativo seria uma boa idéia.
      Pois agora, seus problemas “acabaram-se”, com o novo googlemapeitorparaseuaplicativeitor Tabajara, as coisas finalmente vão acontecer.

      Veja abaixo como fazer para colocar o Google para trabalhar para você, e mostrar para seus clientes, ou para o chefe, como você é esperto (Não tanto quanto o Larry Page e o Sergey Brin, pois eles ganham fortunas com o Google, e você ainda está bem distante de conseguir comprar 1 Boeing só para fazer baladas nos céus como eles fazem).

     Mãos à obra entonces:

     Temos uma tabela de Clientes, 4 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.

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

procedure TForm1.CarregaMapa;
begin
  ShellExecute(0, Nil,
    PChar('http://maps.google.com.br/maps?f=q&source=s_q&hl=pt-BR&geocode=&q=' +Clientes.FieldByName('Logradouro').AsString + ', ' + Clientes.FieldByName('Numero').AsString + ', ' +
      Clientes.FieldByName('Cidade').AsString + '-' + Clientes.FieldByName('UF').AsString + '&jsv=143c&sll=-23.186453,-46.884453' +
      '&sspn=0.478436,0.545883&g=&ie=UTF8&ct=clnk&cd=1'), Nil, Nil, 0);
end;

    Basta chamar a função e será carregado o browser com o mapa correspondente ao endereço passado.

    No próximo post vamos ver como fazer para, além de mostrar o mapa, traçar a rota entre dois endereços.

    Como sempre falo : Google é seu amigo, use-o.
    www.spectrus.com.br

Movendo colunas e linhas em um StringGrid.

 

      Na maioria dos componentes Grids, de terceiros, você pode observar que o usuário pode mover colunas e linhas usando o mouse. Aliás, o usuário espera este comportamento de um Grid. Então, como fazer isso usando um TStringGrid?

      Como sempre, se é isso que você estava querendo implantar em seu aplicativo, mais uma vez “seus pobrêma se acabaram-se”,  apresentamos o incrível GridColumnRowMoveitor Tabajara.

      Mais uma vez… mão na massa e chega de enrolação.

      Primeiro de tudo, se você der uma olhada mais aprofundada no componente TCustomGrid você verá que os métodos MoveColumn e MoveRow estão lá, fazem parte do componente, mas eles estão ocultos no TStringGrid, eles são herdados do ancestral TCustomGrid porém não estão acessíveis no descendente, o motivo?? Ora, vai lá saber o que se passa na cabeça dos garotos da Codegear…

      Como resolver esse problema? Simples e fácil, sem maiores complicações, basta fazer uma herança de TStringGrid e redeclarar estes métodos como public.

type
    TNovoGrid = class(TStringGrid)
    public
    procedure MoveColumn(FromIndex, ToIndex: LongInt);
    procedure MoveRow(FromIndex, ToIndex: LongInt);
   end;

   Para implementar estes métodos é muito simples, basta na implementação, chamar o ancestral e passar para ele o comando:

procedure TNovoGrid.MoveColumn(FromIndex, ToIndex: LongInt);
begin
    inherited;
end;

procedure TNovoGrid.MoveRow(FromIndex, ToIndex: LongInt);
begin
    inherited;
end;

     Você não precisa registrar este componente na paleta de componentes. Use o TStringGrid ou qualquer descendente de TCustomGrid normalmente como já faz hoje, e quando você precisar usar estes métodos, simplesmente faça um typecast (conversão de tipos) para a nova classe, e pronto. Veja o exemplo abaixo:

procedure TForm1.Button1Click(Sender: TObject);
begin
       TNovoGrid(StringGrid1).MoveColumn(2, 5);
end;

    Bom, é isso aí, até a próxima.
    www.spectrus.com.br