31 de agosto de 2010

Design Patterns com Delphi : Interpreter - parte III

Neste post eu concluo o exemplo para o Design Pattern comportamental Interpreter. Conforme eu disse no post anterior, aqui eu falo sobre análise sintática de um texto para podermos trabalhar de fato com o Interpreter.

Embora o conceito estabelecido para o Interpreter nada diga a respeito, é imprescindível construir um analisador sintático para podermos utilizar este Pattern. Independentemente da complexidade da sintaxe válida estabelecida, é o analisador quem extrairá as partes que compõem uma expressão e montará a instância de classe que nos permitirá avaliar tal expressão em um contexto qualquer. Como as sintaxes podem ser muito diferentes entre si - pense num comando SQL comparado à notação polonesa, por exemplo - fica bastante difícil determinar uma fórmula única que sirva de guia para construir todas as possibilidades.

Para o exemplo do saldo de itens de estoque introduzido no post inicial sobre o Interpreter, a sintaxe é bastante simples. Ela consta de alguns poucos símbolos válidos e de apenas duas operações definidas. Formalmente, podemos resumir essa sintaxe como segue:

Expressão ::= { variável [ { + | - } Expressão] }
variável ::= { SALDO | EMFABRICACAO | PEDIDOS }
Ou seja, a expressão pode ser uma única das variáveis permitidas ou uma operação (soma ou subtração) entre a variável e uma outra expressão - isso mostra o nítido caráter recursivo dessa sintaxe. Os nomes de variáveis aceitas na expressão são os listados na segunda linha do quadro acima. O sinal | significa que se deve escolher um dos valores para que a expressão seja válida. O par [ ] indica um termo não obrigatório. Para finalizar a definição da sintaxe, ressalto que ela não é sensível ao caso; isto é, letras maiúsculas e minúsculas não são diferenciadas.

Montar uma instância de classe com base num texto que siga essa sintaxe exigirá preparativos para controlar chamadas recursivas, conforme constatamos anteriormente. Precisamos ter controle sobre qual é o próximo tipo de token esperado (variável ou operação) e também conhecer até que ponto o texto já foi analisado. Veja o código:
function TWMontaExpressao.CreateExprFrom (AStrExpr: String) : TWExpressaoAbstrata;
var lPos, lCompr : integer;
lTipoTokenEsperado : TWTipoToken;
lExpr: TWExpressaoAbstrata;
begin
{ Próximo caractere a ser lido no texto }
lPos := 1;
{ Ainda não há expressões criadas }
lExpr := Nil;
AStrExpr := Trim (AStrExpr);
{ Comprimento do texto analisado }
lCompr := Length (AStrExpr);
{ Inicia aguardando uma variável }
lTipoTokenEsperado := ttkVar;

repeat
try
{ Chama a função recursiva para montar a expressão a partir do texto }
lExpr := CreateProxExpr (AStrExpr, lPos, lTipoTokenEsperado, lExpr);
except
on Exception do begin
{ No caso de erro, devolver a memória usada }
FreeAndNil (lExpr);
Raise;
end;
end;
until (lPos > lCompr);

Result := lExpr;
end;
A função acima apenas prepara o ambiente para que a função CreateProxExpr seja invocada recursivamente. Ela, então, pode percorrer o texto, analisando e interpretando os termos da expressão associada.

Para isso funcionar, a declaração da função recursiva deve ser feita com passagem de parâmetros por referência (palavra chave VAR do Delphi) pois assim o avanço na análise pode ser devolvido em cada chamada à função, garantindo que nada será interpretado duas vezes e que uma chamada prosseguirá a partir do ponto onde a anterior parou. A função também recebe a instância da expressão montada até o momento para que possa utilizá-la na montagem das instâncias seguintes. A definição do cabeçalho da função fica assim:
function CreateProxExpr (var AStrExpr: String;
var APos: integer;
var ATipoTokenEsperado:TWTipoToken;
AExpr: TWExpressaoAbstrata)
: TWExpressaoAbstrata;
A codificação dessa função deve extrair o próximo token do texto e verificar se ele é do tipo esperado - variável ou operação. Se for do tipo variável, basta criar a instância correta de expressão para a variável. No caso de ser uma operação, a própria função será chamada recursivamente para obter o termo seguinte da expressão pois nossas operações sempre exigem dois termos. Com o segundo termo preparado, podemos então criar a instância de classe representando a operação em questão. Fica mais fácil vendo o código:
lToken := ProxToken (AStrExpr, APos, ATipoTokenEsperado);
lTipoToken := CalcTipoToken (lToken);

if (lTipoToken <> ttkVazio) then
begin
if (lTipoToken <> ATipoTokenEsperado) then
raise Exception.CreateFmt('Esperado símbolo do tipo "%s"',
[GetNameFromTipoToken(ATipoTokenEsperado)]);
if (lTipoToken = ttkVar) then
ATipoTokenEsperado := ttkOper { Próximo tem que ser uma operação }
else begin
ATipoTokenEsperado := ttkVar; { Próximo tem que ser uma das variáveis }
lExpr := CreateProxExpr (AStrExpr, APos, ATipoTokenEsperado, AExpr);

if (lExpr = Nil) then
raise Exception.CreateFmt('Fim inesperado da expressão - operador "%s"', [lToken]);
end;

Result := CriaInstancia (lToken, AExpr, lExpr);
end;
Embora não estejam retratadas, o código acima faz uso de outras funções importantes do analisador: a função ProxToken - que apenas percorre o texto a partir da posição atual e extrai dele um token - e a CriaInstancia, que é uma espécie de Factory para criação de instâncias de expressões.

O resultado da chamada à função CreateExprFrom é uma única instância de expressão que pode ser avaliada com qualquer item de estoque do tipo TWItem. Todas as outras instâncias construídas pelo analisador ficam internas a esta instância e é ela a responsável por devolver os recursos de memória quando não forem mais necessários.

O exemplo completo está disponível para download como um projeto Delphi 2005. Para obtê-lo, siga este link.

Mais Informações Design Patterns com Delphi : Interpreter - parte I e parte II, Download do projeto de exemplo em Delphi 2005

26 de agosto de 2010

Design Patterns com Delphi : Interpreter - parte II

Do ponto de vista da construção de classes e seus relacionamentos, a implementação do Design Pattern comportamental Interpreter (introduzido no post anterior) não apresenta grandes desafios.

Como podemos ver no diagrama reproduzido abaixo, há apenas heranças simples e classes acessando normalmente os membros umas das outras.
Diagrama UML para o padrão Interpreter
Para começar, precisamos definir uma classe abstrata para representar a base de todas as expressões. É nessa classe que estabelecemos a interface para a avaliação de expressões, isto é, o nome da função de avaliação, seus parâmetros e tipo de retorno. Essa definição é que torna a classe abstrata, forçando as heranças a providenciarem o funcionamento correto inerente a cada uma delas.

No nosso exemplo, a classe base é a TWExpressaoAbstrata, cuja função para avaliação das expressões chama-se Calcular, que calculará o saldo disponível do item informado no parâmetro. Segue a declaração em Delphi.
{ Expressão Abstrata }
TWExpressaoAbstrata = class
public
function Calcular (AItem: TWItem) : double;virtual;abstract;
end;

{ Expressão Não-Terminal }
TWExpressaoSoma = class(TWExpressaoAbstrata)
protected
_Expr1, _Expr2: TWExpressaoAbstrata;

public
constructor Create (AExpr1, AExpr2: TWExpressaoAbstrata);
destructor Destroy;override;

function Calcular (AItem: TWItem) : double;override;
end;

{ Expressão Terminal }
TWExpressaoSaldo = class(TWExpressaoAbstrata)
public
function Calcular (AItem: TWItem) : double;override;
end;

No trecho acima aparecem ainda as declarações de duas classes que herdam da TWExpressaoAbstrata e que são exemplos de codificação para os tipos de expressões existentes. Cada uma delas fornece uma implementação própria da função Calcular, dependendo do objetivo da classe. Uma delas é a TWExpressaoSaldo, implementando uma expressão terminal (aquela que não depende de outra expressão para poder ser avaliada). A outra classe é a TWExpressaoSoma, que implementa a expressão não-terminal relativa à operação de soma. Esse tipo de expressão depende de outras expressões para ter seu valor calculado, tornando necessária a presença das variáveis internas _Expr1 e _Expr2. Como essas variáveis internas são declaradas com o tipo base abstrato TWExpressaoAbstrata, elas podem receber qualquer tipo de expressão, tanto as terminais quanto outras não-terminais. Vejas a implementação das funções dessas classes :
{ Expressão não-terminal para a operação de Soma }
constructor TWExpressaoSoma.Create (AExpr1, AExpr2: TWExpressaoAbstrata);
begin
_Expr1 := AExpr1;
_Expr2 := AExpr2;
end;

destructor TWExpressaoSoma.Destroy;
begin
FreeAndNil (_Expr1);
FreeAndNil (_Expr2);

inherited;
end;

function TWExpressaoSoma.Calcular (AItem: TWItem) : double;
begin
Result := _Expr1.Calcular(AItem) + _Expr2.Calcular(AItem);
end;

{ Expressão terminal que apenas retorna o saldo físico atual do item }
function TWExpressaoSaldo.Calcular (AItem: TWItem) : double;
begin
Result := AItem._Saldo;
end;

Veja que a função Calcular na operação de soma exige que ambas as instâncias de expressões internas estejam presentes e sejam válidas, já que ambas têm que ser avaliadas antes de terem seus valores somados para produzir o resultado esperado.

Normalmente, seu programa terá referência apenas à expressão de nível mais alto - as demais, se houver, serão instâncias internas a esta de nível mais alto. Isso significa que se esta expressão é não-terminal, então ela é composta por outras expressões às quais a aplicação não tem acesso direto. É por isso que expressões não-terminais têm a responsabilidade de liberar a memória usada pelas expressões internas que as compõem, conforme vemos no destrutor mostrado no trecho de código acima.

A classe TWItem não possui nada de especial, podendo ser até mesmo uma estrutura simples, se for necessário. A única coisa a ressaltar é que as expressões definidas neste exemplo usam o TWItem como contexto, acarretando a obrigatoriedade de que essas expressões conheçam integralmente a definição do item. Com isso, as expressões ficam amarradas a esse contexto envolvendo itens.

No próximo post, eu mostro como codificar uma análise sintática básica a partir de uma expressão textual, criando no processo as instâncias de classes de expressão que resultarão numa única instância passível de ser avaliada com vários contextos distintos.

19 de agosto de 2010

Design Patterns com Delphi : Interpreter - parte I

Apesar de não ser muito comum, pode aparecer entre as manutenções de um ERP a situação em que algo similar a uma linguagem tenha que ser desenvolvida. Por "linguagem" eu quero dizer uma sequência de comandos que sigam uma sintaxe bem definida, isto é, um conjunto de regras que devem ser respeitas quando escrevemos uma expressão nessa linguagem. É o mesmo conceito por trás de linguagens como o Pascal, C#, VB ou SQL em que cada comando dado deve seguir estritamente as regras estipuladas para a respectiva linguagem; caso contrário, a expressão não poderá ser avaliada já que ela não teria um significado associado.

Um exemplo seria o que ocorre com o cálculo de saldo disponível dos itens em estoque. Cada empresa tem um jeito próprio de verificar esse saldo, dependendo da necessidade. Alguns incluem a quantidade que está em produção e abatem a carteira de pedidos; há ainda tratamentos diferentes em relação à mercadoria que esteja "fora de estoque" (em beneficiamento ou em poder de terceiros, por exemplo), variando as combinações possíveis entre os tipos. Com isso, é possível construir uma sintaxe com operações simples - adição e subtração - e os nomes de cada tipo de variável (carteira de pedidos, em produção, etc.) como termos inerentes à linguagem. Se quiser, o usuário informa uma expressão para cada item (ou família de itens) respeitando a sintaxe e obterá o saldo conforme suas necessidades.

Este tipo de situação que envolve a construção de uma sintaxe é abordado pelo Design Pattern comportamental Interpreter, cujo foco está na representação das possíveis operações estabelecidas pela sintaxe. Isso significa que este padrão não se preocupa com a análise sintática de uma expressão mas sim com sua avaliação num determinado contexto, ficando a cargo da aplicação fazer a análise necessária para montar a lista de instâncias de classes que representam a expressão.

Segue um diagrama UML modelando o cenário que envolve cálculo de saldo através do padrão Interpreter, conforme descrito antes:
Diagrama UML para o padrão Interpreter

No quadro abaixo está a nomenclatura normalmente utilizada para as classes participantes da solução:
O Context contem informações globais que delimitam o cenário no qual uma expressão será computada. Cada parte da expressão pode buscar no contexto informações que a ajudem a determinar seu próprio valor. Ou seja, o valor retornado pela avaliação de uma expressão depende do contexto onde ela foi avaliada. A classe TWItem do diagrama tem essa responsabilidade.

AbstractExpression é uma interface que introduz os meios para avaliação de um termo qualquer de uma expressão da linguagem. No exemplo, a interface TWExpressaoAbstrata detem esse papel. Ela introduz a operação Calcular, que recebe um Contexto como parâmetro de modo que a mesma expressão pode ser avaliada em diversos contextos distintos.

TerminalExpression é o nome dado à um termo atômico da expressão, isto é, qualquer termo que não dependa da avaliação de outros termos para ter seu próprio valor avaliado. É o caso de variáveis e constantes numa linguagem. No exemplo desse post, representam expressões terminais as classes TWExpSaldo, TWExpPedidos e TWExpEmFabricacao pois seus valores são calculados com base apenas no contexto.

São classificados como NonterminalExpression os termos de expressões cujo valor depende do cálculo de outros termos. Se encaixam nessa definição as operações de adição e subtração previstas na sintaxe do exemplo; elas não são autocontidas e dependem da avaliação de outros dois termos. No exemplo, as operações citadas são implementadas respectivamente pelas classes TWExpSoma e TWExpSubtracao.

A classe Client é responsável por juntar todos os outros participantes da solução, construindo a árvore de instâncias que representam uma expressão e fornecendo um contexto apropriado para avaliar essa expressão quantas vezes forem necessárias. Este é o papel da classe TWEditaItem no diagrama.

Nos próximos posts eu apresento uma implementação desse diagrama em Delphi e também um exemplo de como analisar uma expressão na sintaxe estabelecida, transformando essa expressão em instâncias de classes que possibilitem avaliá-la em vários contextos.

16 de agosto de 2010

Traçando rotas com a versão 3 da API do Google Maps

Usar a versão 3 da API do Google Maps para traçar a rota entre dois endereços envolve basicamente os mesmos procedimentos descritos no post "Usando a versão 3 da API do Google Maps" aos quais se acrescenta o uso de um objeto específico da API para traçar a rota - o DirectionsService.

Mas, vamos por partes. Como já disse naquele post, a API é disponibilizada como um conjunto de classes e outros códigos JavaScript. Então, o local deste script precisa ser indicado no HTML:

<script type="text/javascript" src="http://maps.google.com/maps/api/js?v=3.1&sensor=false&language=pt-BR"></script>

Também precisaremos de instâncias dos dois objetos principais da API - o que representa o mapa em si e o Geocoder, responsável pela conversão entre endereços reais e suas respectivas coordenadas (latitude/longitude).
var map, geocoder;
function initialize() {
var lOptions = {zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP};
var lMapNode;
lMapNode = document.getElementById('map_canvas');
map = new google.maps.Map(lMapNode, lOptions);
geocoder = new google.maps.Geocoder();
}

function trataLocais (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({map: map, position: results[0].geometry.location });
}
}

Apenas para lembrar, o nome 'map_canvas' identifica uma tag HTML do tipo DIV. Com isso, mostrar um endereço no mapa se restringe ao código javascript abaixo.
var lEndereco;
var lRequest;

lEndereco = 'ALAMEDA SANTOS, 1000, SÃO PAULO - SP, 01418-9028';
lRequest = { address: lEndereco, region: 'BR'};

geocoder.geocode( lRequest, trataLocais);

Até aqui, o processo foi o mesmo descrito no outro post. No entanto, traçar a rota não exige muito mais que isso. Para esse recurso funcionar, temos que instanciar o serviço de cálculo de rotas do Google Maps e o serviço que exibe uma rota calculada. Depois, basta solicitar ao primeiro que trace a rota entre dois endereços fornecidos:
var directionsService;
var directionsRenderer;

directionsService = new google.maps.DirectionsService();

directionsRenderer = new google.maps.DirectionsRenderer();
directionsRenderer.setMap(map);

var enderDe = 'ALAMEDA SANTOS, 1000, SÃO PAULO - SP, 01418-9028';
var enderAte = 'AVENIDA NAÇÕES UNIDAS, 17-17, BAURU - SP, 17013-035';

var request = {
origin:endDe,
destination:endPara,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};

directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsRenderer.setDirections(response);
}
});)

Veja que é o serviço de exibição quem é conectado ao mapa. Como o serviço que traça a rota apenas realiza os cálculos, este não precisa conhecer o mapa que está instanciado. Veja também que foi necessário construir uma requisição com as informações sobre a rota que se deseja traçar. Esta requisição é um objeto do tipo DirectionsRequest, que oferece opções para refinar a rota. Além do endereço de origem para a rota e o seu destino, é exigido da requisição que se estipule o meio de transporte a ser utilizado para ir de um endereço a outro. Nesta versão da API, as possibilidades são ir de carro (como no exemplo), ir de bicicleta ou andando. Outros ajustes disponíveis na requisição permitem configurar a rota para evitar estradas pedagiadas e fornecer rotas alternativas.

Outro ponto a destacar é o segundo parâmetro da função route, responsável pelo cálculo da rota. Este parâmetro é uma função que devemos usar para receber os dados da rota calculada e um status desse cálculo. Caso o status seja OK, basta passar os dados calculados ao serviço de exibição, fechando o ciclo.

Esse procedimento resulta no seguinte mapa:


O HTML com este exemplo pode ser acessado neste link.

Um outro recurso útil a ser adicionado é o que descreve como seguir a rota, da mesma maneira que um aparelho de GPS narra as direções a tomar e distâncias a percorrer. Para isso, crie uma nova tag DIV no seu HTML - digamos que você a identifique como panel. Basta, então, configurar o serviço de exibição, indicando-lhe qual a tag onde descrever a rota. Apenas uma linha, como segue:
directionsRenderer.setPanel(document.getElementById("panel"));


13 de agosto de 2010

Manipulando conteúdo de arquivos PDF

O PDF - Portable Document Format - é um dos formatos de arquivo mais populares para distribuição de documentos. Uma das razões para isso é o fato de que - como o próprio nome diz - esse ser um formato criado para ser "portável". Isso quer dizer que ele poderá ser aberto e lido em praticamente qualquer sistema operacional - Windows, Linux, Mac OS, etc. Ajuda também na popularização a existência de plugins para lê-lo diretamente nos principais navegadores da Internet.

Mas o HTML também é um formato portável, com recursos para criar documentos tão ricos quando o PDF. Por que, então, criar um PDF para publicar um documento na Internet? Acontece que o PDF permitir configurar propriedades extras para, por exemplo, evitar que alguém sem permissão possa alterar o conteúdo ou mesmo copiá-lo. Esse recurso é imprescindível para proteger direitos autorais. Um PDF também pode ser assinado digitalmente, fornecendo uma dupla proteção para previnir alterações não autorizadas.

É corrente a ideia de que criar PDFs profissionais exige a compra de softwares caros, em especial o Acrobat Pro da Adobe, empresa que especificou o formato e desenvolveu as primeiras ferramentas. Mas, de acordo com o Wikipedia, a especificação do PDF foi feita como um padrão aberto e qualquer um pode criar programas para leitura e/ou criação de arquivos nesse padrão. Tal especificação está publicada no site da ISO, podendo ser adquirida neste link.

Por isso, há bons programas gratuitos para criação desses arquivos, tais como o CutePDF Writer que funciona como uma impressora. Isto é, qualquer documento que você mandar nessa impressora gerará um PDF com o conteúdo correspondente.

Já o NitroPDF Reader é mais flexível pois tem uma interface gráfica inspirada no Office 2007 que permite realizar manutenções diretamente no PDF:
NitroPDF Reader

De acordo com o fabricante, o programa é capaz de ler cerca de 300 extensões diferentes de arquivos e convertê-los em PDF, prontos para edição no próprio ambiente da ferramenta. Entre os formatos com os quais ele trabalha estão os do Microsoft Office (Word, Excel), páginas HTML e diversos tipos de imagens.

Esta ferramenta permite que se adicione comentários e anotações no arquivo, recurso útil para trabalhos colaborativos nos quais mais de uma pessoa faz sugestões e correções ao documento. Nesta mesma direção, a ferramenta também tem recursos para destacar trechos do documento e acrescentar textos em qualquer ponto.

É possível ainda a criação de "carimbos" para identificar o autor de uma manutenção ou garantir a procedência do arquivo, numa espécie de assinatura eletrônica embutida no documento final. De acordo com a sensibilidade do conteúdo, esse carimbo pode ser protegido por senha para evitar uso indevido.

Pra quem trabalha com formulários em PDF, o NitroReader permite salvar uma cópia com as informações lançadas nos campos, garantindo o registro de como o formulário foi preenchido.

Um outro recurso bastante interessante é o que permite extrair todo o texto do documento de um modo que a formatação - parágrafos e espaços - não se perca, ao contrário do que ocorre com o Copiar do Acrobat Reader. Isso levanta, no entanto, uma preocupação com seus próprios textos inseridos num PDF: se não quer que seu trabalho seja tão facilmente copiado, a sugestão é não inserí-lo como texto mas como uma imagem.

O NitroPDF Reader está em versão beta e é gratuito. Há uma outra versão chamada NitroPDF Reader Pro mais completa, com outros recursos e facilidades mas esta é paga.

3 de agosto de 2010

Usando a versão 3 da API do Google Maps

A API do Google Maps é uma coleção de classes, estruturas e outros tipos de dados construídos em JavaScript para facilitar o trabalho de manipulação de mapas e rotas em uma página HTML, tanto para exibição nos navegadores tradicionais disponíveis em desktops (Internet Explorer, Firefox, etc.) quanto para dispositivos móveis, cujos recursos são mais limitados.

De acordo com o site do Goggle Maps, a versão 3 foi projetada para ser mais rápida que a anterior, além de dar um suporte melhor à criação de sites com mapas para uso específico em dispositivos móveis (celulares, smartphones, tablets, etc).

Recentemente, a ABC71 passou a usar esse serviço gratuito para exibir no cadastro de Clientes e Fornecedores de seu ERP Omega um mapa com a localização do Cliente ou Fornecedor. A tela também permite que se trace a rota entre a empresa usuária do ERP e seu Cliente ou Fornecedor. Isso foi possível em parte devido à facilidade com que a API permite trabalhar diretamente com endereços reais - a alternativa é o sistema onde temos que fornecer a latitude e longitude do ponto a ser exibido.

O que o Omega faz é preparar um HTML em tempo de execução usando as informações de endereço do Cliente e apresentar esse HTML num navegador embutido na aplicação, segundo a técnica descrita no post Usando "streams" para navegação no TWebBrowser. No presente post eu mostro como construir esse HTML que exibe um endereço.

Todo o conteúdo da API está disponível num único local que precisa ser indicado no HTML de modo que você possa usar as classes e outros símbolos aí definidos. Para isso, basta incluir uma tag script simples na área de cabeçalho do HTML:

<script type="text/javascript" src="http://maps.google.com/maps/api/js?v=3.1&sensor=false&language=pt-BR"></script>

Repare que há alguns parâmetros na referência ao endereço do script, permitindo um ajuste mais fino do uso da API. No exemplo, instruo os servidores do Google a me fornecer a versão 3.1 do script e a usar textos traduzidos para o português brasileiro - veja outras configurações possíveis aqui.

Há dois objetos principais na API que devem ser criados para mostrar um mapa. O primeiro é o mapa em si e o segundo chama-se Geocoder, responsável por converter de forma transparente um endereço real em suas respectivas coordenadas (latitude/longitude), usadas internamente pelo mapa.

var map, geocoder;
function initialize() {
var lOptions = {zoom: 15,mapTypeId: google.maps.MapTypeId.ROADMAP};
var lMapNode;
lMapNode = document.getElementById('map_canvas');
map = new google.maps.Map(lMapNode, lOptions);

geocoder = new google.maps.Geocoder();

mostraEndereco ('AVENIDA NAÇÕES UNIDAS, 17-17, BAURU - SP, 17013-035');
}

function mostraEndereco (pEndereco) {
var lRequest = { address: pEndereco, region: 'BR'};
geocoder.geocode( lRequest, trataLocais);
}

O construtor da classe de mapa aceita 2 parâmetros: o nó HTML onde o mapa será desenhado (no exemplo, uma tag DIV cujo nome é map_canvas) e uma estrutura com a configuração inicial do mapa. No HTML acima eu estabeleço explicitamente as opções de nível de zoom e o tipo de visão desejado. O zoom pode variar de 0 (mais afastado) até 20 (maior proximidade), mas nem todos os níveis de zoom estão disponíveis para todos os endereços. O tipo de visão configurada é o mapa de ruas tradicional - outras opções incluem o uso de imagens de satélite ou uma mistura dos dois tipos, por exemplo. A documentação das opções disponíveis pode ser encontrada aqui.

O construtor do Geocoder em si não possui parâmetros mas sua principal função - geocode - sim. Também são 2: o primeiro é uma estrutura do tipo GeocoderRequest, onde passamos informações sobre o local a ser tratado no mapa. Repare na forma com que passei o endereço, separando logradouro, número, cidade, estado e CEP. O segundo parâmetro é uma função que será chamada automaticamente pelo script com a lista dos endereços encontrados pela função geocode. Isso significa que se os dados do GeocoderRequest forem insuficientes para determinar um lugar único - endereço errado ou não mapeado, por exemplo - pode ser retornado mais de um endereço aproximado. Para esses casos, pode-se optar por fornecer o par latitude/longitude diretamente, garantindo a exatidão e unicidade do local que desejamos apresentar. No entanto, você precisará ter essa informação de antemão para poder repassá-la ao mapa - talvez até mesmo cadastrando-a num banco de dados.

De qualquer forma, a função pode simplesmente exibir o primeiro endereço retornado. Veja um exemplo, onde o primeiro local encontrado é centralizado no mapa e identificado por um marcador:

function trataLocais (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({map: map, position: results[0].geometry.location });
}
}

A partir deste momento, temos criado o vinculo entre o mapa e o Geocoder, resultando no mapa interativo abaixo:

O HTML com o exemplo completo pode ser acessado neste link; ele inclui código para tratar a situação em que mais de um endereço atende os parâmetros informados. Para um exemplo traçando rotas, veja o post Traçando Rotas com a API do Google Maps