APIs e Microsserviços

29 jun, 2010

Google Directions e Elevation API – Aperfeiçoando o cálculo de autonomia de veículos

Publicidade

Olá, amigos! Estou de volta, com muitas idéias e pouco tempo para escrever.

Bom, há algum tempo o Google disponibilizou algumas APIs que eu já sentia falta em 2009, teriam facilitado minha vida um bocado no ano passado. São elas:

Obs.: Verifique os limites de utilização e termos de uso de cada API antes de utiliza-las, tanto academicamente quanto comercialmente. Até a publicação deste artigo, havia apenas documentação em English.

Directions API

A Directions é um serviço que calcula as direções entre dois locais (podendo haver pontos de passagem) através de uma solicitação HTTP, ou seja, para quem já trabalha com a API do Google Maps não é preciso mais de um objeto do tipo GMap para conseguir capturar informações sobre uma determinada rota ou utilizar a Google Maps Javascript API V3 Services (http://code.google.com/intl/en/apis/maps/documentation/javascript/services.html#Directions).

Esta API aceita tanto endereços do tipo texto como também latitudes e longitudes previamente formatadas de acordo com o padrão estabelecido.

Como eu a chamo? No browser, basta digitar a seguinte URL: http://maps.google.com/maps/api/directions/output?parameters

Tipos de Saída (output) Até o momento, XML ou JSON

Como está ficando?

XML: http://maps.google.com/maps/api/directions/xml?parameters

JSON: http://maps.google.com/maps/api/directions/json?parameters

Parameters:

  • origin: Endereço ou latitude e longitude em formato texto da origem da rota.
  • destination: Endereço ou latitude e longitude em formato texto do destino da rota.
  • mode (opcional, o default é de carro): [driving, walking, bicycling] só não te esqueces que o Google não se responsabiliza por onde você irá passar.
  • waypoints (opcional): pontos de passagem, caso queira fazer uma paradinha no meio da rota ou passar por algum local específico, o formato é o mesmo dos parametros de origem e destino.
  • alternative (opcional): Se definido para true, especifica que o serviço pode fornecer alternativas, mais de uma rota. Demora um pouco mais pra responder… Lembre de tratar a resposta depois para o usuário.
  • avoid (opcional): evitar certos locais pré-definidos. Hoje [tolls (estradas com pontes), highways(estradas).
  • language (opcional): já diz tudo, verifique no site as linguas suportadas.
  • sensor: Obrigatório, [true/false] se você esta utilizando um aparelho com sensor de localização (gps, celular, etc…).

Exemplos

http://maps.google.es/maps/api/directions/xml?origin=Av Ipiranga 3200, Porto Alegre – RS&destination=Av Joao Pessoa, Porto Alegre – RS&sensor=false

Lembre-se sempre de utilizar o nome da cidade e estado para uma melhor precisão do local.

Resposta

<?xml version="1.0" encoding="UTF-8"?>
<DirectionsResponse>
<status>OK</status>
<route>
<summary>Av. Ipiranga</summary>
<leg>
<step>
<travel_mode>DRIVING</travel_mode>
<start_location>
<lat>-30.0465300</lat>
<lng>-51.1975700</lng>
</start_location>
<end_location>
<lat>-30.0467000</lat>
<lng>-51.2119800</lng>
</end_location>
<polyline>
<points>xmkvDxonwHqOvJm@p@m@fAe@~AUxBOdXX~Bd@bBdBzDxCpFzK|T</points>
<levels>B??@??@????B</levels>
</polyline>
<duration>
<value>195</value>
<text>3 minutos</text>
</duration>
<html_instructions>Siga na direção &lt;b&gt;noroeste&lt;/b&gt; na &lt;b&gt;Av. Ipiranga&lt;/b&gt; em
direção à &lt;b&gt;R. Vicente da Fontoura&lt;/b&gt; &lt;span style=&quot;white-space: nowrap&quot;&gt;-
siga &lt;b&gt;1,7&amp;nbsp;km&lt;/b&gt;&lt;/span&gt;</html_instructions>
<distance>
<value>1699</value>
<text>1,7 km</text>
</distance>
</step>
<step>
<travel_mode>DRIVING</travel_mode>
<start_location>
<lat>-30.0467000</lat>
<lng>-51.2119800</lng>
</start_location>
<end_location>
<lat>-30.0432600</lat>
<lng>-51.2146000</lng>
</end_location>
<polyline>
<points>znkvDziqwHoTjO</points>
<levels>BB</levels>
</polyline>
<duration>
<value>57</value>
<text>1 min</text>
</duration>
<html_instructions>Vire à &lt;b&gt;direita&lt;/b&gt; na &lt;b&gt;Av. João Pessoa&lt;/b&gt; &lt;span
style=&quot;white-space: nowrap&quot;&gt;- siga &lt;b&gt;450&amp;nbsp;m&lt;/b&gt;&lt;/span&gt;
</html_instructions>
<distance>
<value>458</value>
<text>0,5 km</text>
</distance>
</step>
<duration>
<value>252</value>
<text>4 minutos</text>
</duration>
<distance>
<value>2157</value>
<text>2,2 km</text>
</distance>
<start_location>
<lat>-30.0465300</lat>
<lng>-51.1975700</lng>
</start_location>
<end_location>
<lat>-30.0432600</lat>
<lng>-51.2146000</lng>
</end_location>
<start_address>Av. Ipiranga, 3200 - Petrópolis, Porto Alegre - RS, 90160-001, Brasil</start_address>
<end_address>Av. João Pessoa - Porto Alegre - RS, Brasil</end_address>
</leg>
<copyrights>Map data ©2010 MapLink</copyrights>
<overview_polyline>
<points>xmkvDxonwHqOvJm@p@m@fAe@~AUxBOdXX~Bd@bBdBzDxCpFzK|ToTjO</points>
<levels>B??@??@????@B</levels>
</overview_polyline>
</route>
</DirectionsResponse>

Elevation API

A API Elevação fornece dados de elevação para todas as localizações na superfície da terra, incluindo locais de profundidade no fundo do oceano (que retornam valores negativos). Nos casos em que o Google não possui medidas de elevação na posição exata que você precisa pedir, o serviço irá interpolar e retornar um valor médio mais próximo.

Como funciona na prática? Você passa uma coordenada ou um conjunto de coordenadas e ela lhe responde a altitude de cada ponto.

Para que serve? Existem alguns exemplos de rotas de bicicleta para evitar locais altos, morros. Mas hoje vou mostrar um pequeno exemplo da sua utilização para autonomia e logística.

Como eu a chamo?

http://maps.google.com/maps/api/elevation/outputFormat?parameters

Tipos de Saída (output): até o momento, XML ou JSON

Como está ficando?

XML: http://maps.google.com/maps/api/elevation/xml?parameters

JSON: http://maps.google.com/maps/api/elevation/json?parameters

Parametros:

  • De Posição: locations: {latitude,longitude} Você precisa passar uma latitude e longitude, neste formato: 40.714728,-73.998672 ou mais de um local concatedado por pipes: 40.714728,-73.998672|-34.397,150.644. Ainda existe outro formato, utilizando o Encoded Polyline Algorithm (verificar na documentação da API)
  • ou Posições de Amostras: path: O mesmo que o parametro locations, porém para este caso você precisa definir 2 pontos e utilizar o parametro samples que define a quantidade de pontos entre os locais. Como? É o mesmo que uma rota, você define uma origem e um destino e a quantidade de pontos que deseja no meio da rota. Ele irá lhe retornar os pontos de origem e destino a quantidade de pontos desejada entre os mesmos, informanda o elevação do terreno (pontos em linha reta).
  • sensor: Obrigatório, [true/false] se você está utilizando um aparelho com sensor de localização (gps, celular, etc…).

Exemplos

http://maps.google.com/maps/api/elevation/xml?locations=39.7391536,-104.9847034&sensor=false

<ElevationResponse>
<status>OK</status>
<result>
<location>
<lat>39.7391536</lat>
<lng>-104.9847034</lng>
</location>
<elevation>1608.8402100</elevation>
</result>
</ElevationResponse>

http://maps.google.com/maps/api/elevation/json?locations=39.7391536,-104.9847034|36.455556,-116.866667&sensor=false

{
"status": "OK",
"results": [ {
"location": {
"lat": 39.7391536,
"lng": -104.9847034
},
"elevation": 1608.8402100
}, {
"location": {
"lat": 36.4555560,
"lng": -116.8666670
},
"elevation": -50.7890358
} ]
}

Aperfeiçoando o cálculo de autonomia de veículos

Sem mais delongas, vamos a um exemplo. Porém já aviso de antemão que é apenas uma explanação da idéia, as fontes e maiores detalhamentos sobre a solução não serão disponibilizados, porque o problema não será resolvido na sua totalidade, apenas serão utilizados valores fictícios. Só lembrando, não sou da área de logística, não esperem muito conhecimento disso.

Softwares utilizados:

  • PostgreSQL 8.4 + PostGIS + PlPython
  • Python 2.6
  • Quantum GIS

Breve descrição do problema:

O problema que imaginei para utilização destas duas APIs é o simples cálculo de autonomia que qualquer pessoa nos dias de hoje utiliza, além é claro de empresas de logística. A pergunta básica é quantos km/litro seu carro faz? Na cidade? Na estrada? Que tal ampliarmos esta pergunta e calcularmos também em determinada rota?

O problema apresentado, bem como sua solução, não é novidade. As empresas especializadas em logística muitas vezes possuem bases de dados com informações quanto a elevação do terreno e já se utilizam de alguns cálculos semelhantes para tomada de decisão, porém com a criação destas APIs, nós, meros mortais, temos acesso a estas informações.

Objetivo

  • Capturar para uma determinada rota as elevações do terreno.
  • Identificar possíveis ladeiras/declives onde o consumo de combustível é maior/menor.
  • Realiza o cálculo de consumo de acordo com este trajeto e suas peculiaridades.

Objetivo Principal: abrir horizontes! Este não um artigo passo a passo de como fazer.

Solucionando

Caminho a ser percorrido:

  • Origem: -30.0330800,-51.2161500, Rua Tomaz Flores, Bom Fim – Porto Alegre – RS
  • Destino: -51.2153200,10.1994400, Rua Santo Antonio, Bom Fim – Porto Alegre – RS

Esta trajeto foi selecionado por cruzar com a AvenidaêIndependencia, ponto mais alto do bairro Bomfim, em Porto Alegre.

Como o que me interessa é apenas a autonomia do meu veículo para este trajeto, sem precisar de um relatório visual, irei criar uma função diretamente no banco de dados para solucionar o problema.

Para isto, vou utilizar o PostrgreSQL com a extensão espacial PostGIS, adicionado da biblioteca PlPython para criação de procedures que acessam as APIs do Google diretamente do banco de dados.

Inicialmente crie a função directions no banco, que irá acessar a API Directions do Google, passando como parâmetros origem e destino, montando a URL que irá retornar o XML. Depois, apenas trato os parâmetros de retorno e chamo uma nova função elevation, buscando a elevação do terreno para cada trecho encontrado.

CREATE OR REPLACE FUNCTION directions(origin character varying, destination character varying)
RETURNS character varying AS
$BODY$

try:
        params = {"origin": origin, "destination" : destination, "sensor" : "false"}
query = urllib.urlencode(params)
  url = "http://maps.google.com/maps/api/directions/xml?%s" % query
(...)
sql = 'SELECT elevation('%s,%s|%s,%s',%s,%s)' % (start_lat, start_lng, stop_lat, stop_lng, vald, val);
plpy.execute(sql, 5)
return;
(...)

except:
return 'erro excp';

$BODY$
LANGUAGE 'plpythonu' VOLATILE STRICT
COST 100;
ALTER FUNCTION directions(character varying, character varying) OWNER TO postgres;

A função elevation funciona da mesma maneira, porém é acionada diversas vezes, de acordo com a quantidade de pontos/rotas/steps que o trajeto possui.

CREATE OR REPLACE FUNCTION elevation(string character varying, samples integer, distance integer)
  RETURNS character varying AS
$BODY$

try:
params = {"path": string, "samples" : samples, "sensor" : "false"}
            query = urllib.urlencode(params)
            url = "http://maps.google.com/maps/api/elevation/xml?%s" % query
(...)
while x < total:
(...)
sql = 'INSERT INTO t_consumo (id,lat,lng,alt,distance) VALUES (%d,'%s','%s','%s','%s')'
% (id,lat,lng,elevation,distance)
plpy.execute(sql, 5)
(...)
return 'ok';
except:
return 'erro excp';

$BODY$
LANGUAGE 'plpythonu' VOLATILE STRICT
COST 100;
ALTER FUNCTION elevation(character varying, integer, integer) OWNER TO postgres;

Como resultado tenho uma tabela chamada t_consumo populada com as coordenadas da minha rota e suas respectivas elevações.

t_consumo (lat, lng, elevação)

  • “-30.0330800″;”-51.2161500″;”18.5466881″
  • “-30.0329859″;”-51.2161432″;”18.9679661″
  • “-30.0328917″;”-51.2161363″;”19.3892441″
  • “-30.0327976″;”-51.2161295″;”19.8105221″
  • “-30.0327034″;”-51.2161227″;”20.2318001″
  • “-30.0326093″;”-51.2161159″;”20.6530762″
  • “-30.0325151″;”-51.2161090″;”21.0743542″

Obs.: Os pontos geográficos foram dispersos a 10m de um para outro, ou seja, o cálculo de angulação é realizado nesta distância.

Como vou calcular?

Bom, neste momento parto do princípio que meu veículo faz 6 km/litro na cidade, em linha reta, vamos apenas utilizar valores para exemplificar.

Para definir a inclinação do trajeto, neste momento vou pegar a elevação do próximo ponto menos a elevação do ponto que estou, o resultado pode ser positivo (subida) ou negativo (descida) de acordo com o andamento do trajeto.

SELECT  t1.gid AS tab1, 
t2.gid AS tab2,
        transform(setsrid(makeline(geometry('POINT('||t1.lng||' '||t1.lat||')'),
geometry('POINT('||t2.lng||' '||t2.lat||')')),4326),4291) AS the_geom,
    (t2.alt::numeric - t1.alt::numeric) as alt_rest,
    round(((t2.alt::numeric - t1.alt::numeric)*-2)+6,2) as autonomia from teste t1, teste t2
WHERE (t1.gid+1) = (t2.gid)

O resultado desta consulta nada mais é que os campos indicadores de cada ponto, a geometria da linha (ponto que estou e próximo ponto), altitude restante (elevação da linha) e a autonomia do meu veículo para este deslocamento (neste cálculo, parto do principio que meu veículo possui um ganho ou perda de 2 km/litro para angulações superiores e inferiores ao plano. Este é um dado que deve ser enquadrado de acordo com o tipo de veículo, por exemplo, meu carro faz 6km/litro em linha reta, 4km/litro em subidas de ~25° e 8km/litro em descidas com o mesmo declive de ~25°).

Pode-se apenas utilizar o -1 como fator determinante de angulação do declive.

Exemplo:

Subida

  • Altitude que estou: 17.8
  • Próximo altitude: 18.2
  • Total: 18.2 – 17.8 = 0,4 -tan (tangente) = ~21° (Como é positivo, subida), multiplique por -1 para diminuir a autonomia padrão do veículo

Autonomia: 6 + (0,4*-1) = 5,6 km/litro

Descida

  • Altitude que estou: 18.5
  • Próximo altitude: 18.2
  • Total: 18.2 –
    18.5 = -0,3 -tan (tangente) = ~-16° (Como é negativo, descrida), multiplique por -1
    para diminuir a autonomia padrão do veículo

Autonomia: 6 + (-0,3*-1) = 6,3 km/litro


Resultado do trajeto no Google Maps e Quantum GIS:


Fonte: Google Maps – Camada Earth

Na imagem abaixo, a coluna à esquerda, em azul, são os valores da elevação do terreno para cada ponto dentro da linha. As informações a direita são quanto ao resultado da autonomia do meu veículo neste mesmo ponto.

A legenda abaixo representa os intervalos de valores selecionados e as cores designadas.

  1. Elevação do Terreno (em metros)
  2. Consumo Km/Litro no trecho

Legenda quantumgis

O ponto branco representa o local de menor elevação, enquanto o ponto preto representa o local de maior elevação, no nosso trajeto a Av Independencia. A linha representa o consumo litros/km, verde é o menor consumo e vermelho o maior.

Podemos ver que a região com maior declive é a que antecede a Av Independencia, ainda na Rua Thomaz Flores, onde a autonomia do veículo se encontra no seu menor valor.

Concluimos com isso, obviamente, que não é a elevação do ponto que reduz a autonomia do veículo, e sim a inclinação da via naquele determinado trajeto.

Resultado

Consumo geral do meu veículo: 6 km/litro

Para este trajeto:

SELECT avg(consumo) AS media FROM t_consumo

Igual: 6.16 km/litro

Anexos

Imagem: Consumo na rota, utilize para comparar com as imagens abaixo.

foto1.png
Foto1: Após Interseção R. José Otão, inicio do declive.


Foto2: Declive superior da Rua Tomaz Flores, maior consumo do trajeto.

Foto3: Av Independencia, ponto de maior elevação porém plano, consumo moderado/médio.

Foto4: Declive Rua Santo Antonio, aumento de autonomia.

Feito, abraço.