31 de janeiro de 2012

Informações sobre rotas com a versão 3 da API do Google Maps

No ano passado eu publiquei dois posts mostrando como traçar rotas usando a versão 3 da API do Google Maps. O primeiro tratava de um cenário básico onde consideramos um endereço de origem e outro de destino; o segundo avançava um passo ao permitir a inclusão de um ou mais pontos intermediários ao trajeto. Por razões distintas, várias pessoas me perguntaram como é possível obter a distância total entre a origem e o destino em uma rota, independendo se vamos efetivamente exibir a rota traçada.

Pra refrescar a memória, o quadro a seguir ilustra os comandos e estruturas básicos necessários pra trabalharmos com uma rota.
var directionsService, directionsRenderer;
var enderDe, enderAte;

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

enderDe = 'ALAMEDA SANTOS, 1000, SÃO PAULO - SP, 01418-9028';
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);
}
});)

A linha directionsRenderer.setDirections (response) usa o objeto response para traçar automaticamente a rota no mapa. Esse objeto é do tipo DirectionsResult e é montado pela função route antes de ser repassado como response à nossa função de callback. No entanto, você pode usar manualmente as informações contidas em response sem que a rota precise ser exibida.

De acordo com a documentação, o DirectionsResult possui como única propriedade um Array chamado routes contendo a lista de rotas encontradas pelo serviço para atender a requisição feita. Cada elemento dessa lista traz detalhes de uma rota específica, descrita na forma de um objeto DirectionsRoute, o que inclui detalhes de cada trecho do trajeto, como a distância de cada um (em metros) e o tempo estimado usando o tipo de locomoção escolhido.

Com isso, podemos facilmente obter a distância a ser percorrida para chegar ao destino usando a rota em questão:
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var distancia;
var rota = response.routes[0]; /* Primeira rota */
var etapa = rota.legs[0]; /* única etapa dessa rota */

distancia = etapa.distance.value;

alert ('Distância Total => ' + distancia.toString() + ' metros.');
}
});)

A propriedade legs destrincha cada etapa do trajeto usando objetos do tipo DirectionsLeg para descrevê-las. Por etapa entende-se o deslocamento necessário entre dois endereços. Assim, se você estipulou apenas a origem e o destino, haverá uma única etapa e, por conseguinte, uma única posição em legs. Por outro lado, se você adicionou pontos de referência usando o waypoints na requisição (veja esse post para mais detalhes), haverá uma nova etapa descrevendo como chegar a cada ponto. Nesse caso, será preciso percorrer todas as posições, somando a distância de cada etapa para se obter a distância total da rota:
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var distancia = 0;
var rota = response.routes[0]; /* Primeira rota */
var etapa, i;

for (i = 0; i < rota.legs.length; i++)
{
/* Obtem cada etapa */
etapa = rota.legs[i];
distancia += etapa.distance.value;
}

alert ('Distância Total => ' + distancia.toString() + ' metros.');
}
});)

O detalhamento do caminho a ser seguido dentro de cada etapa do trajeto pode ser acessado através da propriedade steps. Cada passo também possui sua distância mas a soma delas já está consolidada na própria etapa (leg). Tenha em mente que, para certos trajetos, pode acontecer de as informações de distância e duração não estarem disponíveis. Por questão de simplicidade, os scripts acima não levam isso em conta. Um exemplo completo dos recursos mostrados até agora pode ser visto na página abaixo, cujo código fonte está neste link:

Um outro ponto importante a ser lembrado é que a função route é assíncrona. Isto significa que a função de callback que passamos a ela poderá ser executada depois da linha de código imediatamente seguinte ao route. Assim, se tiver que comparar as distâncias de várias rotas, terá que montar uma forma de sincronizar os cálculos para que a comparação só ocorra depois da execução de todos os callbacks envolvidos. Esse efeito normalmente é conseguido através da função javascript setinterval, verificando a cada segundo, por exemplo, se as rotas já estão prontas.

Mais Informações
Traçando rotas com a versão 3 da API do Google Maps - Parte I e Parte II, Google Maps JavaScript API V3

23 de janeiro de 2012

Trabalhando com Ações em Delphi e C++ Builder

As operações que um usuário pode executar em programas com interface visual podem estar disponíveis de diversas maneiras. As formas mais óbvias e mais utilizadas são os menus, barras de ferramentas, menus de contexto, botões espalhados pela interface e, mais recentemente, os ribbons introduzidos pela interface do Microsoft Office. Com essa variedade de formas de se acessar uma operação, é bastante comum que esse acesso esteja disponível em mais de um lugar.

Do ponto de vista do programador surge então uma questão: qual a melhor maneira de se gerenciar as operações em uma aplicação, notadamente aquelas que estarão acessíveis por mais de uma via ? A solução para quem desenvolve com Delphi ou C++ Builder é um componente que já existe há um bom tempo nesses ambientes, chamado TActionManager.

Como diz o nome, o objetivo do TActionManager é gerenciar as operações que estarão disponíveis para o usuário em uma aplicação, centralizando a configuração básica de cada operação. O componente permite organizar as operações por afinidade (relativas a arquivo, edição, visualizar ou ajuda, por exemplo); informar textos e imagens descritivos da operação a fim de manter a uniformidade visual independente da forma de acesso escolhida; sinalizar se a operação está ativa ou mesmo visível.

E como é que isso tudo se conecta ? Alimentar o TActionManager com operações faz com que o IDE crie instâncias de um outro objeto, o TAction. Cada TAction corresponde a uma operação - ou ação - isolada, independente da interação necessária para ativá-la. Essa ação pode então ser associada à propriedade Action existente em diversos componentes - itens de menu, botões, itens em barras de ferramenta, etc.

Entretanto, certos componentes - como o TActionMainMenuBar e o TRibbon - são configurados diretamente com uma instância do ActionManager e não com ações individuais.

Para dar manutenção nas ações gerenciadas pelo ActionManager, dê um duplo clique nele. Será exibida uma caixa de diálogo como a reproduzida abaixo, onde é possível criar novas ações, excluí-las e rearranjá-las. Selecionar uma ação fará com que o Object Inspector apresente as propriedades relativas a ela.
Configuração do Action Manager
Há umas poucas propriedades no ActionManager e elas não são obrigatórias; destaco abaixo aquelas que são mais utilizadas:
Images e DisabledImages são TImageList contendo, respectivamente, a imagem que deve ser vinculada a uma ação quando ela está ativa e quando está desabilitada. Em ambos os casos, as imagens devem ser pequenas - 16 x 16 pixels. Uma das propriedades de uma ação é o índice da imagem a ser usada; como só há um índice, a imagem correspondente à ação deve estar exatamente na mesma posição em todas as listas de imagens configuradas.
LargeImages e LargeDisabledImages são os TImageList que contem imagens grandes - 32 por 32 pixels - para as ações configuradas nesse gerenciador. A primeira lista vincula imagens a ações ativas enquanto a segunda traz as imagens grandes para ações desabilitadas. Lembre-se que as imagens relativas a uma ação devem estar na mesma posição, conforme dito no tópico anterior.
State: Permite desativar todas as ações de um única vez, impedindo-as de serem disparadas.
Use a propriedade ActionBars para criar barras de ferramentas associadas ao gerenciador.
FileName indica o caminho completo para um arquivo que armazernará a configuração dinâmica feita pelo usuário final em tempo de execução.

Como se depreende do exposto acima, o gerenciador de ações pode ser usado em conjunto com o TCustomizeDlg para permitir ao usuário final reorganizar as barras de ferramentas.

Uma vez que o gerenciador está preparado, podemos acrescentar-lhe as ações e configurá-las. A lista abaixo mostra as principais propriedades do TAction:
Use Caption para informar o texto padrão para a ação. É esse texto que será usado num item de menu ou como texto de um botão onde a ação for vinculada.
Use Category para organizar as ações, agrupando-as por afinidade, o que facilita a manuntenção de ações tanto para o programador quanto para o usuário final - caso a funcionalidade de configuração seja disponibilizada a ele.
ImageIndex é a posição das imagens a serem usadas para representar a ação em diversos contextos. Cada ação terá à sua disposição o mesmo índice em cada uma das 4 listas existentes no gerenciador de ações.
Uma combinação de teclas pode ser atribuída em Shortcut. Essa combinação é um método alternativo para executar a ação associada a ela.
O evento OnExecute é usado para centralizar a execução da ação. Assim, não é preciso acrescentar código em nenhum outro ponto do programa nem responder a outros eventos; basta configurar a propriedade Action existente no componente que desejar disparar a ação - menus e botões, por exemplo.
Enabled permite habilitar ou desabilitar uma ação individual de forma centralizada. Isto é, altere essa propriedade para que todas as possibilidades de se executar a ação sejam afetadas de uma só vez.
Modifique a propriedade Visible para esconder ou mostrar uma ação de uma só vez em todos os contextos nos quais ela estiver disponível.