19 de dezembro de 2012

Personalizando a apresentação de mapas com o Google Maps

Na maioria das vezes, as informações apresentadas num mapa produzido através da API do Google Maps são suficientes para que o usuário rapidamente consiga ver um endereço e seu entorno ou a rota a ser seguida para ir de um ponto a outro. Em certas situações, no entanto, pode ser necessário adicionar ao mapa outras informações para melhor transmitir a ideia correta que se deseja.

Esse tipo de necessidade foi exemplificado quando falei sobre o uso de marcações num mapa para apontar locais de interesse nas proximidades de um determinado endereço. O exemplo pode ser encontrado neste link.

A API do Google Maps nos permite acrescentar informações extras aos mapas através do conceito de camadas que são sobrepostas ao mapa, chamadas de overlays. Para isso, a API disponibiliza um conjunto de classes javascript. Além da já citada Marker, há classes para traçar linhas, delimitar áreas circulares ou retangulares e até mesmo para projetar uma imagem personalizada sobre uma área do mapa.

O overlay mais simples é o Polyline, usado para traçar linhas por sobre o mapa. O quadro abaixo traz código javascript que une com uma linha reta 2 pontos do mapa: a sede da ABC71 e a praça da Sé, em São Paulo:
var mapOptions = {zoom: 10,mapTypeId: google.maps.MapTypeId.ROADMAP};
map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
geocoder = new google.maps.Geocoder();

var endDe = new google.maps.LatLng(-23.566146,-46.652579);
var endAte = new google.maps.LatLng(-23.550563,-46.633101);
var request = { location: endDe, region: 'BR'};

geocoder.geocode (request, function(response, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.fitBounds (new google.maps.LatLngBounds(endDe, endAte));
/* ... */
var pontos = new google.maps.MVCArray ();
pontos.push (endDe);
pontos.push (endAte);

var rect = new google.maps.Polyline ({
path: pontos,
map:map,
strokeWeight: 5,
strokeColor: 'blue',
visible: true
});
}
});
Após as inicializações de praxe para o ambiente do Maps, o código solicita a geocodificação do endereço da ABC71 (na verdade, sua latitude/longitude).
Em resposta à conclusão da geocodificação, isto é, assim que o mapa com o endereço solicitado está pronto, eu crio uma lista MVCArray e adiciono a ela os dois pontos que definem a reta desejada.

Em seguida, instancio a classe Polyline propriamente dita, fornecendo-lhe a lista de pontos. Também indico o mapa onde a linha será traçada (map), a espessura da linha em pixels (strokeWeight) e a cor dela (strokeColor). As cores podem ser informadas tanto pelo nome HTML quanto pela notação RGB hexadecimal.

Posso, por exemplo, demarcar um bairro inteiro com esse recurso? Sim, mas você terá que montar a lista de pontos o mais completa possível para que o desenho resultante tenha uma precisão adequada. O Polyline produz linhas abertas; para traçar linhas fechadas com uma quantidade arbitrária de pontos use a classe Polygon.

A função fitBounds, usada no início da resposta, apenas aplica um nível de zoom apropriado para garantir que ambos os endereços sejam exibidos num único quadro, sem que o usuário precise interagir com ele.

O código analisado produz o mapa a seguir:
Imagine agora o caso de uma pizzaria que queira divulgar num mapa o raio de ação onde ela faz entregas. O overlay produzido pela classe Circle permite dar destaque a uma área circular, dado um centro e um raio em metros. Veja o exemplo:
var endDe = new google.maps.LatLng(-23.566146,-46.652579);
var request = { location: endDe, region: 'BR'};

geocoder.geocode (request, function(response, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(endDe);
/* ... */
var circleOpts = ({
map: map,
center: endDe,
editable: false,
fillColor: 'yellow',
fillOpacity: 0.4,
radius: 3500,
strokeColor: '#0099ff',
strokeWeight: 4,
visible: true });
var circle = new google.maps.Circle (circleOpts);
}
});
Como no caso da linha, para o círculo também valem as propriedades que indicam o mapa, a cor do traço e sua espessura. Mas, ao invés de uma lista de pontos, informamos apenas o centro; no código anterior, usei o próprio endereço da ABC71 para isso. A definição do círculo fica completa com a propriedade radius - o raio, fornecido em metros; no exemplo, o raio terá 3500 metros (ou 3,5 Km).

Outra configuração disponível diz respeito ao preenchimento da área delimitada pelo círculo. A propriedade fillColor determina a cor do preenchimento enquanto a fillOpacity controla o grau de transparência, sendo que o valor 1 significa totalmente opaco (o mapa sob o círculo não é visto) e o valor 0 suspende o preenchimento.

A propriedade editable, quando ativa, permite ao usuário redimensionar o círculo. Eu a desliguei pois ela não faz sentido no contexto do exemplo. O resultado é o mapa exibido no quadro abaixo:

Os mesmos conceitos valem para a classe Rectangle. Além disso, é permitido trabalhar com vários overlays num mesmo mapa, se for necessário. Os exemplos publicados nesse post podem ser acessados respectivamente nos seguintes links : Overlay com Linha e Overlay com Círculo.

7 comentários :

Anônimo disse...

Luis, parabêns pelo blog e as dicas com gmaps! Show de bola. Estou montando um recurso no sistema da empresa que trace a rota de linhas de ónibus no mapa. Usei uma vez com polilyne, mas deu muito trabalho porque precisei montar ponto a ponto onde o onibus entra.

Acha que existe solução mais prática para quebrar nas ruas ou não usar o trajeto padrão do google?

E já viu alguma forma de verificar se dentro de um trajeto por exemplo existe um determinado ponto (marcador com lat e long)? Nunca vi isso mas gostaria de consultar se existe x paradas de onibus (marcadores) por exemplo.
Obrigado!

Luís Gustavo Fabbro disse...

Dependendo da cidade, a rota através de transporte público (ônibus, metrô, trem, etc.) pode ser traçada automaticamente pelo Maps. Basta informar os endereços de origem e de destino e colocar como travelMode o valor google.maps.DirectionsTravelMode.TRANSIT. Todas as rotas alternativas usando transporte público serão retornadas para você poder tratar.

Quanto aos marcadores, desconheço que haja um modo pronto de retornar essa informação a partir da rota. Creio que você terá que manter uma lista de marcadores e tentar cruzar manualmente com as informações da rota.

[]s

Flávio CS disse...

Parabéns pelo post. Só um comentário, o link para o Overlay com Círculo está direcionando para o Overlay com Linha, poderia corrigir por favor? Obrigado e parabéns novamente.

Luís Gustavo Fabbro disse...

Flávio

Obrigado pelo aviso; já corrigi o link e o exemplo do Overlay com Círculo agora pode ser acessado no fim do texto corretamente.

[]s

Flávio CS disse...

Luís, só mais um ponto. Estou criando uma apresentação com mais de 100 Overlays com círculo, e notei que o google limita a 5 Overlays ao mesmo tempo. Teria uma forma de contornar isso? Obrigado.

Luís Gustavo Fabbro disse...

Flávio

Fiz um teste e não consegui reproduzir o seu problema. Isto é, consegui incluir mais de 100 círculos no mapa sem problemas. Certifique-se de que os endereços colocados estão perto o suficiente uns dos outros para poderem ser visualizados simultaneamente mas não tão perto a ponto de ser impossível distingui-los entre si.

PS: É possível que haja uma limitação mas me parece ser bem maior do que 5 overlays.

[]s

MrDiniz disse...

Olá Luíz.. é isso mesmo que estou fazendo.. :
function calcRoute() {
var myTrip=[];
var bounds = new google.maps.LatLngBounds();
/* var start;
var inicio; */

var dt = '${listaCoord.dtSistema}';
var cod = '${listaCoord.codDaf}';
var lat = '${listaCoord.idLatitude}';
var lng = '${listaCoord.idLongitude}';

var pt = new google.maps.LatLng(lat, lng);

myTrip.push(pt);
locations.push(pt);
datas.push(dt);
bounds.extend(pt);
/* Verifica o ponto mais recente */
/* Verifica o ponto de partida (início) */
/*Cria ponto*/


//atual = pt;
createMarkerAtual(pt,cod,map);



//inicio = pt;
createMarkerInicio(pt,cod,map);



createMarker(pt,cod,map);





var flightPath = new google.maps.Polyline({
path:myTrip,
strokeColor:"#0000FF",
strokeOpacity:0.5,
strokeWeight:4
});

flightPath.setMap(map);
map.fitBounds(bounds);
codeLatLng();
}

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.