Skip to content

Latest commit

 

History

History
151 lines (107 loc) · 6.43 KB

README.md

File metadata and controls

151 lines (107 loc) · 6.43 KB

MapoGPS

Utilitário para a transformação de coordenadas geográficas para posições no Guia Mapograf.

É... o quê?

Até há não tantos anos atrás, não existia Waze e nem Google Maps. O que todo mundo usava, era o bom e velho guia de ruas.

Pessoalmente, eu não gosto de uma voz dizendo onde eu tenho que virar enquanto estou dirigindo, e tampouco fico confortável em acreditar cegamente no caminho que um app decide para mim. Também não gosto da forma como as ruas são exibidas nesses apps, prefiro a forma como elas são apresentadas no guia. Então sim, apesar de minha não muita idade (sou de 1996), ainda carrego um guia Mapograf comigo.

Então, fiz este pequeno utilitário para converter coordenadas geográficas de um GPS na posição em um guia, pelos seguintes motivos:

  • eu estava entediado;
  • eu pude trazer um pouco de tecnologia a algo mais tradicional.

Como isso é feito?

Sistema de coordenadas do guia

As páginas que contém os mapas são numeradas de A1 a A99, de B1 a B27 e de 1 a 432. Para o posicionamento dentro de cada página, há uma numeração de 1 a 30 representando as linhas, e de A a Z (com algumas letras faltando...) representando as colunas.

Overview da conversão

A conversão é feita em três etapas:

  1. Conversão da latitude e longitude em coordenadas em um plano, cuja origem é o Marco Zero de São Paulo, e usando metros como unidade;

  2. Conversão desse plano intermediário em um outro plano, cujas unidades são a largura e a altura de uma página do guia, e a origem é o encontro do meridiano mais a oeste com o paralelo mais ao norte do guia;

  3. Conversão para páginas e posições do guia, a partir do plano definido na etapa 2.

O desenvolvimento, porém, eu fiz na seguinte ordem:

A etapa 3

A etapa 3 é simples: no guia, antes das páginas referentes ao mapa em si no há um pequeno mapa mostrando sua a abrangência. Nele, é mostrada em que página cada porção da área abrangida é mostrada. É possível ver, assim, qual o posicionamento das páginas, e qual página dá continuidade a outra.

A partir dele, foi feito a matriz que está no arquivo pages.py. Note que nessa matriz pages há também páginas sem nome, e que representam áreas que o guia não apresenta, mas que estão dentro dos limites de abrangência. A partir da matriz pages, defini um plano cartesiano, em que a origem é o canto superior esquerdo do que seria a primeira página da primeira linha da matriz (e que é uma página que não existe). O eixo das ordenadas desse plano tem como unidade a altura de uma página, e o eixo das abscissas tem como unidade a largura de uma página.

Desta forma, é simples fazer a transformação de uma posição no guia para esse plano cartesiano e vice-versa: a Catedral da Sé, por exemplo, fica em 151 Z 3. Como a página 151 fica na linha 13 e na coluna 15, sabemos que a parte inteira da posição no plano é (13, 15). Já a parte decimal é calculada a partir da posição dentro da página, Z 3, ou seja, a posição no plano é (13.0834, 15.975).

A etapa 1

Converter coordenadas geográficas para metros apresenta alguns problemas, e para facilitá-los tive que fazer algumas suposições.

Supondo que a terra é uma esfera perfeita, um grau de latitude tem o mesmo comprimento em qualquer local da superfície do planeta: aproximadamente 10001.965729 km.

O comprimento de um grau de longitude, porém, varia de acordo com a latitude. A fórmula para o cálculo do tamanho em metros de um radiano de longitude é dada pela seguinte função:

m(lat) = cos(lat) * R

sendo R o raio da Terra em metros, e lat a latidude em radianos.

Para calcular o tamanho médio de um radiano de longitude entre dois pontos de latitudes diferentes, basta dividir a integral de m entre as latitudes e dividí-la pela diferença entre as latitudes. A partir dele, podemos calcular a o tamanho médio de um grau, ou seja:

(π * R / 180) * (sen(π * b/180) - sen(π * a / 180)) / (b - a)

sendo a e b as duas latitudes, em graus.

Uma vez calculado o tamanho médio de um grau de longitude, basta multiplicá-lo pela variação de longitude para obter a variação em metros no sentido norte-sul. E basta multiplicar o tamanho de um grau de latitude pela variação de latitude para obter a variação em metros no sentido oeste-leste. Ufa!

Agora faço uma segunda suposição: a Terra ser plana na cidade de São Paulo. Da mesma forma que a suposição feita anteriormente, isso vai produzir erros, mas que serão de ordem irrelevante para esta aplicação.

A partir dessa suposição, é calculada a posição relativa do ponto em relação ao Marco Zero de São Paulo, em metros. Desta forma, podemos posicioná-lo em um plano cartesiano que toma como origem o Marco Zero de São Paulo, adotando o metro como unidade.

A etapa 2

Na etapa 3 foi definido um plano cartesiano que representa o guia, e na etapa 1 foi definido um plano no sistema métrico. Esta etapa consiste em converter um plano em outro, e é, portanto, a parte principal da conversão.

A conversão entre os dois planos é feita através de uma transformação linear. A matriz de transformação, porém, é desconhecida.

Para descobrir a matriz de transformação, foram amostrados vários pontos no guia Mapograf, e tomadas suas coordenadas geográficas. Esses pontos estão no arquivo pointset.py. A posição desses pontos nos planos foi calculada, de acordo com as definições descritas anteriormente. Tendo em mãos as posições em ambos os planos, é feita uma regressão linear, que nos dá uma matriz de transformação que minimiza o erro da transformação de um plano em outro. Eba!

Execução

Dependências

  • Python 3
  • Numpy

Uso

python3 mapo_gps.py

As coordenadas geográficas devem ser passadas na forma decimal através da stdin, e serão devolvidas as páginas e posições de página correspondentes.

Erros?

Este programa foi feito para meu uso pessoal, e estou compartilhando ele aqui. Não sou especialista em GIS, nem em álgebra linear, e nem em machine learning.

Se você encontrar algo errado no código ou mesmo neste README, ficarei feliz em ouví-lo em uma issue ou um pull request.