Fork me on GitHub

Você está aqui: Home Dive Into HTML5


Você Está Aqui
(Assim Como Todo Mundo Está)

exibir índice analítico

Mergulhando

Geolocalização é a arte de descobrir aonde você está no mundo e (opcionalmente) compartilhar essa informação com as pessoas que você confia. Há mais de uma maneira de descobrir aonde você está — seu endereço IP, sua conexão de rede sem fio, com que torre de celular seu telefone está falando, ou hardware GPS dedicado que calcula latitude e longitude da informação enviada por satélites no céu.

Pergunte ao Professor Marcação

P: Geolocalização parece assustador. Posso desligá-la?
R: Privacidade é uma preocupação óbvia quando se está falando sobre compartilhar sua localização física com um servidor web remoto. A API de geolocalização diz explicitamente: “User Agents não devem enviar informação de localização para sites na Web sem a expressa permissão do usuário.” Em outras palavras, compartilhar a sua localização é sempre opcional. Se você não quiser, você não tem que fazê-lo.

API de geolocalização

A API de geolocalização permite que você compartilhe sua localização em sites confiáveis. A latitude e longitude são disponibilizadas na página via JavaScript, que por sua vez pode ser enviado a um servidor web e fazer coisas como encontrar locais ao seu redor ou mostrar sua posição em um mapa.

Como você pode ver na tabela a seguir, a API de geolocalização é suportada pela maioria dos navegadores desktop e dispositivos móveis. Além disso, vários navegadores e dispositivos antigos podem oferecer suporte via bibliotecas de terceiros, como veremos depois neste capítulo.

Suporte da API de geolocalização
IE Firefox Safari Chrome Opera iPhone Android
9.0+ 3.5+ 5.0+ 5.0+ 10.6+ 3.0+ 2.0+

Junto com o suporte padrão da API de geolocalização, há tambem várias APIs de dispositivos específicos em outras plataformas móveis. Falarei sobre tudo isso ainda neste capítulo.

Mostre-me o código

A API de geolocalização centraliza tudo em volta da sua nova propriedade global chamada navigator com o objeto: navigator.geolocation.

O simples uso da API de geolocalização se parece com isso:

function get_location() {
  navigator.geolocation.getCurrentPosition(show_map);
}

Porém isso não detecta o posicionamento (latitude e longitude), não há tratamento de erros e não retorna opções. Você pode incluir uma verificação para detectar se há suporte para a API de geolocalização. Para detectar se há suporte, você pode usar o Modernizr:

function get_location() {
  if (Modernizr.geolocation) {
    navigator.geolocation.getCurrentPosition(show_map);
  } else {
    // no native support; maybe try Gears?
  }
}

 ⇜ EU POSSO TER GEOLOCALIZAÇÃO?

O que você faz sem o suporte da geolocalização é contigo. Eu explicarei como fazer um fallback em um minuto, mas antes eu quero falar sobre o que acontecerá durante a chamada do método getCurrentPosition(). Como eu mencionei no começo deste capítulo, o suporte da geolocalização é opcional. Isso significa que seu browser nunca irá forçar você revelar sua localização física atual para um servidor remoto. A experiência do usuário é diferente de browser pra browser. No Mozilla Firefox, a chamada do método getCurrentPosition() da API de geolocalização fará com que o browser mostre uma “barra de notificação” no topo da janela do navegador. Essa barra se parece com isso:

Geolocation opt-in infobar

Há muita coisa acontecendo aqui. Você, como um usuário final,

Além disso, essa barra é

Você acabou de ver o código em JavaScript com que fez essa barra de notificação aparecer. É uma chamada de função única que tem uma função de callback (no qual chamei de show_map). A chamada do método getCurrentPosition() retornará a localização imediatamente, mas isso não quer dizer que você terá a localização do usuário. O primeiro lugar que é garantido para você o acesso a localização do usuário é na função de callback. A função de callback se parece com isso:

function show_map(position) {
  var latitude = position.coords.latitude;
  var longitude = position.coords.longitude;
  // let's show a map or do something interesting!
}

A função de callback será chamada com um único parâmetro, e retornará um objeto com duas propriedades: coords e timestamp. O timestamp é somente a data e o horário de quando a localização foi calculada. (Uma vez que tudo isso está acontecendo de forma assíncrona, você poderá realmente não saber quando irá acontecer com antecedência. Pode demorar algum tempo para que o usuário visualize a barra de notificação e aceite compartilhar sua localização. Dispositivos com GPS dedicado podem demorar mais tempo para se conectar com o satélite do GPS. E assim por diante.) O objeto coords possui propriedades como latitude e longitude que são exatamente o que parece ser: a localização física do usuário no mundo.

Posição do Objeto
Propriedade Tipo Observação
coords.latitude double graus decimais
coords.longitude double graus decimais
coords.altitude double ou null metros em elipsoide de referência
coords.accuracy double metros
coords.altitudeAccuracy double ou null metros
coords.heading double ou null graus em sentido do norte verdadeiro
coords.speed double ou null metros/segundos
timestamp DOMTimeStamp como um objeto de Date()

boy riding a bicycle

Somente três de uma das propriedades são garantidas (coords.latitude, coords.longitude, e coords.accuracy). O restante pode voltar null, dependendo da capacidade do seu dispositivo e a distância que o servidor está de você. As propriedades heading e speed serão calculadas baseadas na posição anterior do usuário, se possível.

Lidando com erros

Geolocalização é complicado. Coisas podem dar errado. Eu havia dito que o “consentimento do usuário” é estranho. Se sua aplicação precisa da localização do usuário mas o usuário não permitir, você está ferrado. O usuário sempre ganha. Se ele não permitir, o que apareceria como mensagem? Veja o segundo parâmetro do método getCurrentPosition(): um erro é chamado na função de callback.

navigator.geolocation.getCurrentPosition(
  show_map, handle_error)

Se qualquer coisa sair errado, sua função callback de erro será chamada com um objeto PositionError.

Objeto PositionError
Propriedade Tipo Observações
code short um valor enumerado
message DOMString não usado para usuários finais

A propriedade code retornará um desses resultados

Seja educado na derrota

function handle_error(err) {
  if (err.code == 1) {
    // user said no!
  }
}

Pergunte ao professor marcação

P: A API de geolocalização pega na Estação Espacial Internacional, na Lua, ou em outros planetas?
R: Os estados específicos da geolocalização, “O sistema de referência geográfica de coordenadas usado pelos atributos é referente ao World Geodetic System (2d) [WGS84]. Nenhum outro sistema de referência é suportado.” A Estação Espacial Internacional está em órbita na Terra, então os astronautas na estacão podem dizer suas localizações por latitude, longitude e altitude. No entanto, o World Geodetic System é centralizado somente na Terra, por isso não podemos usar para saber localizações na Lua ou em outros planetas.

Escolhas! Eu quero escolhas!

Alguns aparelhos — como iPhone e Android — suportam dois métodos para mostrar sua localização. O primeiro método triangula a sua posição baseando-se na sua localização relativa das diferentes torres da sua operadora de celular. Este método é rápido e não necessita de qualquer hardware de GPS dedicado, mas ele só pega uma ideia aproximada de onde você está.

young girl pointing

O segundo método atualmente usa algum hardware de GPS dedicado em seu aparelho para se comunicar com algum satélite de GPS dedicado que está orbitando na Terra. O GPS normalmente pode identificar a sua localização a poucos metros. O lado negativo de um chip de GPS dedicado em seu aparelho é que consome muita energia, então telefones e outros dispositivos geralmente desligam esse chip quando precisa. Isso significa que terá um atraso quando o chip for inicializado para se conectar com o satélite. Se você sempre usa o Google Maps em um iPhone ou outro smartphone, você já viu os dois métodos em ação. Primeiro você vê um grande círculo que aproxima sua posição (procurando uma torre de celular próxima), então um círculo menor (triangulando com outras torres de celulares), então um único ponto com sua posição exata (pego por um satélite de GPS).

A razão de eu falar isso é que, dependendo da sua aplicação web, você talvez não terá uma grande precisão. Se você estiver somente procurando por alguns cinemas nas proximidades, uma precisão menor provavelmente será o suficiente pra você. Não há muitas salas de cinema, mesmo em cidades maiores, e você provavelmente vai listar mais de uma. Por outro lado, se voce está querendo direções em tempo real, você realmente terá que saber onde o usuário está exatamente para poder dizer “vire à direita em 20 metros” ou qualquer outra coisa.

O método getCurrentPosition() tem como um terceiro argumento opcional, um objeto PositionOptions. Existem três propriedades que você pode definir no objeto PositionOptions. Todas as propriedades são opcionais. Você pode definir qualquer uma ou todas, ou nenhuma delas.

Objeto PositionOptions
Propriedade Tipo Padrão Observações
enableHighAccuracy Booleano false true pode ser mais lento
timeout long (sem padrão) em millisegundos
maximumAge long 0 em millisegundos

A propriedade enableHighAccuracy é exatamente o que se parece. Se for verdadeiro, e o dispositivo tiver suporte para tal, e o usuário permitir compartilhar sua posição, então o dispositivo irá tentar fornecer uma precisão maior. Tanto iPhones quanto Androids tem uma permissão separada para baixa e alta precisão, por isso, é possível que a chamada getCurrentPosition() com enableHighAccuracy:true possa falhar, mas chamar com enableHighAccuracy:false poderá dar certo.

A propriedade timeout é um número em millisegundos que sua aplicação irá esperar pela posição. Este tempo não será contado antes do usuário dar permissão para tentar calcular sua posição. Não é o tempo do usuário; é o tempo da rede.

A propriedade maximumAge permite que os dispositivos respondam imediatamente com uma posição em cache. Por exemplo, vamos chamar o método getCurrentPosition() por um período, o usuário permitiu, e a função de callback chamou a posição que foi calculada exatamente às 10:00 horas da manhã, você chamou o método getCurrentPosition() novamente com a propriedade maximumAge em 75000.

navigator.geolocation.getCurrentPosition(
  success_callback, error_callback, {maximumAge: 75000});

O que você está dizendo é que você não precisa necessariamente da posição atual do usuário. Você provavelmente ficará satisfeito sabendo onde ele esteve em 75 segundos atrás (75000 millisegundos). O dispositivo sabe onde o usuário foi em 60 segundos atrás (60000 millisegundos), porque sua posição foi calculada antes da primeira chamada do método getCurrentPosition(). Então o dispositivo não precisa recalcular o localização do usuário novamente. Ele somente retorna exatamente a mesma informação que foi retornada na primeira chamada: mesma latitude, mesma longitude, mesma precisão, e mesmo timestamp (10:00 da manhã).

velho homem olhando em seu telescópio

Antes que você pergunte pela localização do usuário, deve-se pensar sobre o quanto de precisão você precisa, e definir enableHighAccuracy de acordo. Se você precisa encontrar sua localização mais de uma vez, você deve pensar sobre como uma informação antiga pode vir ser útil, e definir maximumAge de acordo. Se você precisa encontrar sua localização continuamente, então o método getCurrentPosition() não é pra você. Você precisa utilizar o método watchPosition().

O método watchPosition() tem a mesma estrutura que o método getCurrentPosition(). Tem duas funcões de callback, uma necessária para sucesso e uma opcional para qualquer erro que possa dar, e também pode ter um objeto opcional PositionOptions que terá todas as mesmas propriedades que você aprendeu. A diferença é que sua função de callback irá ser chamada toda vez que a localização do usuário mudar. Não há necessidade de pesquisar sua posição toda hora. O dispositivo irá determinar o melhor intervalo pra pesquisa da localização, e ele irá chamar sua função de callback sempre que a posição do usuário alterar. Você pode usar isto para atualizar um marcador em um mapa, fornecendo instruções sobre onde deve ir, ou o que quiser.

O método watchPosition() retornará sempre um número. Você provavelmente deve guardar este número em algum lugar. Se você quiser parar de ver a mudança de localização do usuário, você pode chamar o método clearWatch() e passar este número, e o dispositivo irá parar de chamar a funcão de callback. Isso funciona de forma parecida com as funções Javascript setInterval() e clearInterval().

E o IE?

Antes da versão 9 (tecnicamente 9.0RC1), o Internet Explorer não tem suporte para a API de geolocalização da W3C. Mas não se desespere! Gears é um plugin de código aberto da Google que funciona em Windows, Mac, Linux, Windows Mobile, e Android, que fornece recursos para os navegadores antigos. Um desses recursos que o Gears fornece é a API de geolocalização. Ele não é bem a API de geolocalização da W3C, mas tem o mesmo resultado.

Enquanto estamos em um assunto de plataformas legadas, gostaria de deixar bem claro que as plataformas antigas tem suas próprias APIs de geoloalização. BlackBerry, Nokia, Palm, e OMTP BONDI todas fornecem suas próprias APIs. É claro, todas elas trabalham de forma diferente do Gears, que por sua vez funciona de forma diferente da API de geolocalização da W3C. Wheeeeee!

geo.js ao resgate

geo.js é uma biblioteca JavaScript, de código aberto, com licença MIT que facilita o uso entre as diferentes APIs de geolocalização da W3C, a Gears API, e as APIs fornecidas pela próprias plataformas. Para usá-la, você irá precisar adicionar dois scripts no rodapé da sua página. (Tecnicamente, você poderia colocar eles em qualquer lugar, mas scripts no <head> vão fazer com que sua página carregue mais devagar. Então, não faça isso!)

O primeiro script é o gears_init.js, que inicializará o Gears caso estiver instalado. O segundo script é o geo.js.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Dive Into HTML5</title>
</head>
<body>
  ...
  <script src="gears_init.js"></script>
<script src="geo.js"></script>
</body>
</html>

 ⇜ Não deixe isso no seu <head>

Agora você está pronto para usar qualquer API de geolocalização.

if (geo_position_js.init()) {
  geo_position_js.getCurrentPosition(geo_success, geo_error);
}

Vamos começar devagar. Primeiro, você precisa chamar o método init(). O método init() retorna true se o suporte para a API de geolocalização estiver disponível.

if (geo_position_js.init()) {

Chamando o método init() ele não irá buscar sua localização. Ele somente verifica que é possível procurar sua localização. Para encontrar sua localização, você precisa chamar o método getCurrentPosition().

  geo_position_js.getCurrentPosition(geo_success, geo_error);

O método getCurrentPosition() irá fazer seu navegador pedir sua permissão para encontrar e compartilhar sua localização. Se sua geolocalização está sendo fornecida pelo Gears, irá aparecer uma janela perguntando se você permite o Gears utilizar alguns dados. Se seu navegador tem suporte nativo para geolocalização, a janela que irá aparecer, é diferente. Por exemplo, o Firefox 3.5 tem suporte nativo para geolocalização. Se você tentar procurar sua localização no Firefox 3.5, aparecerá uma barra de notificação no topo da página perguntando se você quer compartilhar sua localização com este site.

O método getCurrentPosition() passa dois parâmetros, ambos funções. Se o método getCurrentPosition() conseguiu encontrar sua localização — isto é, se você deu permissão, a API de geolocalização irá funcionar como mágica — a função passada no primeiro parâmetro será chamada. Neste exemplo, a função de callback de sucesso é chamada de geo_success.

  geo_position_js.getCurrentPosition(geo_success, geo_error);

A função de callback tem um único parâmetro, e que contém a posição do usuário.

Chamada de sucesso

function geo_success(p) {
  alert("Encontrado na latitude " + p.coords.latitude +
        ", longitude " + p.coords.longitude);
}

Se o método getCurrentPosition() não encontrar sua localização — porque você não deu permissão, ou a API de geolocalização falhou por algum motivo — será chamada a função no segundo parâmetro. Neste exemplo, o callback de falha é chamado de geo_error.

  geo_position_js.getCurrentPosition(geo_success, geo_error);

O callback de falha não tem parâmetros

Chamada de erro

function geo_error() {
  alert("Não conseguimos encontrar você!");
}

geo.js não tem suporte para o método watchPosition(). Se você precisar da localização contínua, irá precisar ativar o método getCurrentPosition() você mesmo.

Um exemplo completo e real

Aqui está um exemplo real usando a geo.js para tentar pegar sua localização e mostrar um mapa de onde você está quase que instantaneamente:

homem com um globo na cabeça
Seu navegador suporta geolocation. Clicar para ver sua localização.

Como isso funciona? Vamos dar uma olhada. No carregamento da página, esta página chama geo_position_js.init() para determinar se a geolocalização está disponível em qualquer uma das interfaces que geo.js suporta. Se tiver suporte, ele cria um link que você pode clicar para ver sua localização. Clicando no link é chamado então o método lookup_location(), aqui está:

function lookup_location() {
  geo_position_js.getCurrentPosition(show_map, show_map_error);
}

Se você permitir rastrear sua localização, e o serviço de backend conseguir determinar sua localização, geo.js chama a primeira função de callback, show_map(), com um único parâmetro, loc. O objeto loc tem uma propriedade coords no qual contém sua latitude, longitude e precisão. (Este exemplo não usa a precisão.) O resto do método show_map() usa a API de mapas do Google Maps para mostrar um mapa.

function show_map(loc) {
  $("#geo-wrapper").css({'width':'320px','height':'350px'});
  var map = new GMap2(document.getElementById("geo-wrapper"));
  var center = new GLatLng(loc.coords.latitude, loc.coords.longitude);
  map.setCenter(center, 14);
  map.addControl(new GSmallMapControl());
  map.addControl(new GMapTypeControl());
  map.addOverlay(new GMarker(center, {draggable: false, title: "Você está aqui (mais ou menos)"}));
}

Se geo.js não conseguir determinar sua localização, ele chamará o segundo callback, show_map_error().

function show_map_error() {
  $("#live-geolocation").html('Não foi possível determinar sua localização.');
}

Leitura Adicional

Isso foi “Você Está Aqui (Assim Como Todo Mundo Está)” e Outras Palavras Bonitas; Consulte o Sumário, caso queira continuar com a leitura.

Você sabia?

Em associação a Google Press, O’Reilly está distribuindo este livro em variados formatos, incluindo papel, ePub, Mobi, DRM-free e PDF. A edição paga é chamada “HTML5: Up & Running” e está disponível agora. Este capítulo está incluído na versão paga.

Se você gostou deste capítulo e quer mostrar sua apreciação, basta comprar o livro “HTML5: Up & Running” com esse link afiliado ou comprar a edição eletrônica diretamente da O’Reilly. Você vai ganhar um livro, e eu vou ganhar um trocado. Atualmente, não aceito doações diretas.

Copyright MMIX–MMXI Mark Pilgrim