Tenho dados com latitude e longitude armazenados em meu banco de dados SQLite e desejo obter os locais mais próximos dos parâmetros que coloquei (por exemplo, minha localização atual - lat / lng etc.).
Eu sei que isso é possível no MySQL, e fiz algumas pesquisas que o SQLite precisa de uma função externa personalizada para a fórmula Haversine (calcular a distância em uma esfera), mas não encontrei nada que seja escrito em Java e funcione .
Além disso, se eu quiser adicionar funções personalizadas, preciso do org.sqlite
.jar (para org.sqlite.Function
) e isso adiciona um tamanho desnecessário ao aplicativo.
O outro lado disso é, eu preciso da função Order by do SQL, porque exibir a distância sozinha não é um grande problema - eu já fiz isso no meu SimpleCursorAdapter personalizado, mas não consigo classificar os dados, porque não tenho a coluna de distância em meu banco de dados. Isso significaria atualizar o banco de dados toda vez que o local mudar e isso seria um desperdício de bateria e desempenho. Portanto, se alguém tiver alguma ideia sobre como classificar o cursor com uma coluna que não está no banco de dados, eu também ficaria grato!
Eu sei que existem toneladas de aplicativos Android por aí que usam essa função, mas alguém pode explicar a mágica.
A propósito, encontrei esta alternativa: Consultar para obter registros com base no Radius no SQLite?
Ele está sugerindo fazer 4 novas colunas para os valores de cos e sin de lat e lng, mas existe alguma outra maneira não tão redundante?
fonte
Respostas:
1) Primeiro filtre seus dados SQLite com uma boa aproximação e diminua a quantidade de dados que você precisa avaliar em seu código java. Use o seguinte procedimento para este propósito:
Para ter um limite determinístico e um filtro de dados mais preciso, é melhor calcular 4 locais que estão em
radius
metros a norte, oeste, leste e sul de seu ponto central em seu código Java e então verificar facilmente por menos de e mais de Operadores SQL (>, <) para determinar se seus pontos no banco de dados estão naquele retângulo ou não.O método
calculateDerivedPosition(...)
calcula esses pontos para você (p1, p2, p3, p4 na imagem).E agora crie sua consulta:
COL_X
é o nome da coluna no banco de dados que armazena os valores de latitude eCOL_Y
serve para longitude.Portanto, você tem alguns dados próximos ao seu ponto central com uma boa aproximação.
2) Agora você pode fazer um loop nesses dados filtrados e determinar se eles estão realmente próximos do seu ponto (no círculo) ou não usando os seguintes métodos:
Aproveitar!
Usei e customizei esta referência e concluí.
fonte
A resposta de Chris é realmente útil (obrigado!), Mas só funcionará se você estiver usando coordenadas retilíneas (por exemplo, referências de grade UTM ou OS). Se estiver usando graus para lat / lng (por exemplo, WGS84), o acima só funciona no equador. Em outras latitudes, você precisa diminuir o impacto da longitude na ordem de classificação. (Imagine que você está perto do pólo norte ... um grau de latitude ainda é o mesmo que em qualquer lugar, mas um grau de longitude pode ser de apenas alguns pés. Isso significa que a ordem de classificação está incorreta).
Se você não estiver no equador, pré-calcule o fator de correção, com base em sua latitude atual:
Em seguida, peça por:
((<lat> - LAT_COLUMN) * (<lat> - LAT_COLUMN) + (<lng> - LNG_COLUMN) * (<lng> - LNG_COLUMN) * <fudge>)
Ainda é apenas uma aproximação, mas muito melhor do que o primeiro, portanto, imprecisões na ordem de classificação serão muito mais raras.
fonte
((<lat> - LAT_COLUMN) * (<lat> - LAT_COLUMN) + (<lng> - LNG_COLUMN) * (<lng> - LNG_COLUMN) * <fudge>)
ser menor quedistance
oudistance^2
?Sei que isso foi respondido e aceito, mas pensei em acrescentar minhas experiências e soluções.
Embora eu estivesse feliz em fazer uma função de haversine no dispositivo para calcular a distância precisa entre a posição atual do usuário e qualquer local de destino específico, havia a necessidade de classificar e limitar os resultados da consulta em ordem de distância.
A solução menos do que satisfatória é retornar o lote e classificar e filtrar após o fato, mas isso resultaria em um segundo cursor e muitos resultados desnecessários sendo retornados e descartados.
Minha solução preferida era passar em uma ordem de classificação dos valores delta quadrados de long e lats:
Não há necessidade de fazer o haversine completo apenas para uma ordem de classificação e não há necessidade de raiz quadrada dos resultados, portanto, o SQLite pode lidar com o cálculo.
EDITAR:
Essa resposta ainda está recebendo amor. Funciona bem na maioria dos casos, mas se você precisar de um pouco mais de precisão, verifique a resposta de @Teasel abaixo, que adiciona um fator "fudge" que corrige imprecisões que aumentam conforme a latitude se aproxima de 90.
fonte
cos(latitude)
para que a latitude e a longitude sejam aproximadamente iguais. Consulte en.wikipedia.org/wiki/…A fim de aumentar o desempenho tanto quanto possível, sugiro melhorar a ideia de @Chris Simpson com a seguinte
ORDER BY
cláusula:Nesse caso, você deve passar os seguintes valores do código:
E você também deve armazenar
LAT_LON_SQ_SUM = LAT_COL^2 + LON_COL^2
como coluna adicional no banco de dados. Preencha-o inserindo suas entidades no banco de dados. Isso melhora um pouco o desempenho ao extrair uma grande quantidade de dados.fonte
Experimente algo assim:
Depois disso, id contém o item que você deseja do banco de dados para que você possa buscá-lo:
Espero que ajude!
fonte