Como calcular a caixa delimitadora para uma determinada localização lat / lng?

101

Eu dei uma localização definida por latitude e longitude. Agora eu quero calcular uma caixa delimitadora dentro de, por exemplo, 10 quilômetros daquele ponto.

A caixa delimitadora deve ser definida como latmin, lngmin e latmax, lngmax.

Preciso disso para usar a API panoramio .

Alguém conhece a fórmula de como obter esses pontos?

Edit: Gente, estou procurando uma fórmula / função que leva lat & lng como entrada e retorna uma caixa delimitadora como latmin & lngmin e latmax & latmin. Mysql, php, c #, javascript é bom, mas também o pseudocódigo deve funcionar.

Edit: Não estou procurando uma solução que me mostre a distância de 2 pontos

Michal
fonte
Se você estiver usando um geodatabase em algum lugar, eles certamente possuem um cálculo de caixa delimitadora integrado. Você pode até ir checar a fonte do PostGIS / GEOS, por exemplo.
Vinko Vrsalovic

Respostas:

60

Eu sugiro aproximar localmente a superfície da Terra como uma esfera com raio dado pelo elipsóide WGS84 na latitude dada. Suspeito que o cálculo exato de latMin e latMax exigiria funções elípticas e não produziria um aumento apreciável na precisão (o WGS84 é em si uma aproximação).

Minha implementação é a seguinte (escrita em Python; não testei):

# degrees to radians
def deg2rad(degrees):
    return math.pi*degrees/180.0
# radians to degrees
def rad2deg(radians):
    return 180.0*radians/math.pi

# Semi-axes of WGS-84 geoidal reference
WGS84_a = 6378137.0  # Major semiaxis [m]
WGS84_b = 6356752.3  # Minor semiaxis [m]

# Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
def WGS84EarthRadius(lat):
    # http://en.wikipedia.org/wiki/Earth_radius
    An = WGS84_a*WGS84_a * math.cos(lat)
    Bn = WGS84_b*WGS84_b * math.sin(lat)
    Ad = WGS84_a * math.cos(lat)
    Bd = WGS84_b * math.sin(lat)
    return math.sqrt( (An*An + Bn*Bn)/(Ad*Ad + Bd*Bd) )

# Bounding box surrounding the point at given coordinates,
# assuming local approximation of Earth surface as a sphere
# of radius given by WGS84
def boundingBox(latitudeInDegrees, longitudeInDegrees, halfSideInKm):
    lat = deg2rad(latitudeInDegrees)
    lon = deg2rad(longitudeInDegrees)
    halfSide = 1000*halfSideInKm

    # Radius of Earth at given latitude
    radius = WGS84EarthRadius(lat)
    # Radius of the parallel at given latitude
    pradius = radius*math.cos(lat)

    latMin = lat - halfSide/radius
    latMax = lat + halfSide/radius
    lonMin = lon - halfSide/pradius
    lonMax = lon + halfSide/pradius

    return (rad2deg(latMin), rad2deg(lonMin), rad2deg(latMax), rad2deg(lonMax))

EDITAR: o código a seguir converte (graus, primos, segundos) em graus + frações de um grau e vice-versa (não testado):

def dps2deg(degrees, primes, seconds):
    return degrees + primes/60.0 + seconds/3600.0

def deg2dps(degrees):
    intdeg = math.floor(degrees)
    primes = (degrees - intdeg)*60.0
    intpri = math.floor(primes)
    seconds = (primes - intpri)*60.0
    intsec = round(seconds)
    return (int(intdeg), int(intpri), int(intsec))
Federico A. Ramponi
fonte
4
Conforme apontado na documentação da biblioteca CPAN sugerida, isso faz sentido apenas para halfSide <= 10km.
Federico A. Ramponi
1
Isso funciona perto dos pólos? Parece que não, porque parece que termina com latMin <-pi (para o pólo sul) ou latMax> pi (para o pólo norte)? Acho que quando você está na metade do lado de um pólo, você precisa retornar uma caixa delimitadora que inclui todas as longitudes e as latitudes calculadas normalmente para o lado oposto ao pólo e no pólo próximo ao pólo.
Doug McClean
1
Aqui está uma implementação de PHP da especificação encontrada em JanMatuschek.de: github.com/anthonymartin/GeoLocation.class.php
Anthony Martin
2
Eu adicionei uma implementação C # desta resposta abaixo.
Ε Г И І И О
2
@ FedericoA.Ramponi qual é o haldSideinKm aqui? não entendo ... o que devo passar nesses argumentos, o raio entre dois pontos no mapa ou o quê?
53

Escrevi um artigo sobre como encontrar as coordenadas delimitadoras:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

O artigo explica as fórmulas e também fornece uma implementação Java. (Também mostra por que a fórmula de Federico para a longitude mín. / Máx. É imprecisa.)

Jan Philip Matuschek
fonte
4
Eu fiz uma porta PHP de sua classe GeoLocation. Ele pode ser encontrado aqui: pastie.org/5416584
Anthony Martin
1
Fiz o upload no github agora: github.com/anthonymartin/GeoLocation.class.php
Anthony Martin
1
Isso ao menos responde à pergunta? Se tivermos apenas 1 ponto de partida, não podemos calcular a distância do grande círculo, como é feito neste código, que requer duas localizações de latitude longa.
mdoran3844
há um código inválido em sua variante C #, por exemplo public override string ToString():, é muito ruim substituir esse método global apenas para um propósito, melhor apenas adicionar outro método, em seguida, substituir o método padrão, que pode ser usado em outras partes do aplicativo, não para o gis exato ...
Aqui está um link atualizado para a porta PHP da classe GeoLocaiton de Jan: github.com/anthonymartin/GeoLocation.php
Anthony Martin
34

Aqui, converti a resposta de Federico A. Ramponi para C # para qualquer pessoa interessada:

public class MapPoint
{
    public double Longitude { get; set; } // In Degrees
    public double Latitude { get; set; } // In Degrees
}

public class BoundingBox
{
    public MapPoint MinPoint { get; set; }
    public MapPoint MaxPoint { get; set; }
}        

// Semi-axes of WGS-84 geoidal reference
private const double WGS84_a = 6378137.0; // Major semiaxis [m]
private const double WGS84_b = 6356752.3; // Minor semiaxis [m]

// 'halfSideInKm' is the half length of the bounding box you want in kilometers.
public static BoundingBox GetBoundingBox(MapPoint point, double halfSideInKm)
{            
    // Bounding box surrounding the point at given coordinates,
    // assuming local approximation of Earth surface as a sphere
    // of radius given by WGS84
    var lat = Deg2rad(point.Latitude);
    var lon = Deg2rad(point.Longitude);
    var halfSide = 1000 * halfSideInKm;

    // Radius of Earth at given latitude
    var radius = WGS84EarthRadius(lat);
    // Radius of the parallel at given latitude
    var pradius = radius * Math.Cos(lat);

    var latMin = lat - halfSide / radius;
    var latMax = lat + halfSide / radius;
    var lonMin = lon - halfSide / pradius;
    var lonMax = lon + halfSide / pradius;

    return new BoundingBox { 
        MinPoint = new MapPoint { Latitude = Rad2deg(latMin), Longitude = Rad2deg(lonMin) },
        MaxPoint = new MapPoint { Latitude = Rad2deg(latMax), Longitude = Rad2deg(lonMax) }
    };            
}

// degrees to radians
private static double Deg2rad(double degrees)
{
    return Math.PI * degrees / 180.0;
}

// radians to degrees
private static double Rad2deg(double radians)
{
    return 180.0 * radians / Math.PI;
}

// Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
private static double WGS84EarthRadius(double lat)
{
    // http://en.wikipedia.org/wiki/Earth_radius
    var An = WGS84_a * WGS84_a * Math.Cos(lat);
    var Bn = WGS84_b * WGS84_b * Math.Sin(lat);
    var Ad = WGS84_a * Math.Cos(lat);
    var Bd = WGS84_b * Math.Sin(lat);
    return Math.Sqrt((An*An + Bn*Bn) / (Ad*Ad + Bd*Bd));
}
Ε Г И І И О
fonte
1
Obrigado, este trabalho para mim. Tive que testar o código manualmente, não sabia como escrever um teste de unidade para isso, mas ele gera resultados precisos com o grau de precisão que eu preciso
mdoran3844
qual é o haldSideinKm aqui? não entendo ... o que devo passar nesses argumentos, o raio entre dois pontos no mapa ou o quê?
@GeloVolro: Essa é a metade do comprimento da caixa delimitadora que você deseja.
Ε Г И І И О
1
Você não precisa necessariamente escrever sua própria classe MapPoint. Há uma classe GeoCoordinate em System.Device.Location que leva Latitude e Longitude como parâmetros.
Lawyerson
1
Isso funciona lindamente. Eu realmente aprecio a porta C #.
Tom Larcher de
10

Escrevi uma função JavaScript que retorna as quatro coordenadas de uma caixa delimitadora quadrada, dadas uma distância e um par de coordenadas:

'use strict';

/**
 * @param {number} distance - distance (km) from the point represented by centerPoint
 * @param {array} centerPoint - two-dimensional array containing center coords [latitude, longitude]
 * @description
 *   Computes the bounding coordinates of all points on the surface of a sphere
 *   that has a great circle distance to the point represented by the centerPoint
 *   argument that is less or equal to the distance argument.
 *   Technique from: Jan Matuschek <http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates>
 * @author Alex Salisbury
*/

getBoundingBox = function (centerPoint, distance) {
  var MIN_LAT, MAX_LAT, MIN_LON, MAX_LON, R, radDist, degLat, degLon, radLat, radLon, minLat, maxLat, minLon, maxLon, deltaLon;
  if (distance < 0) {
    return 'Illegal arguments';
  }
  // helper functions (degrees<–>radians)
  Number.prototype.degToRad = function () {
    return this * (Math.PI / 180);
  };
  Number.prototype.radToDeg = function () {
    return (180 * this) / Math.PI;
  };
  // coordinate limits
  MIN_LAT = (-90).degToRad();
  MAX_LAT = (90).degToRad();
  MIN_LON = (-180).degToRad();
  MAX_LON = (180).degToRad();
  // Earth's radius (km)
  R = 6378.1;
  // angular distance in radians on a great circle
  radDist = distance / R;
  // center point coordinates (deg)
  degLat = centerPoint[0];
  degLon = centerPoint[1];
  // center point coordinates (rad)
  radLat = degLat.degToRad();
  radLon = degLon.degToRad();
  // minimum and maximum latitudes for given distance
  minLat = radLat - radDist;
  maxLat = radLat + radDist;
  // minimum and maximum longitudes for given distance
  minLon = void 0;
  maxLon = void 0;
  // define deltaLon to help determine min and max longitudes
  deltaLon = Math.asin(Math.sin(radDist) / Math.cos(radLat));
  if (minLat > MIN_LAT && maxLat < MAX_LAT) {
    minLon = radLon - deltaLon;
    maxLon = radLon + deltaLon;
    if (minLon < MIN_LON) {
      minLon = minLon + 2 * Math.PI;
    }
    if (maxLon > MAX_LON) {
      maxLon = maxLon - 2 * Math.PI;
    }
  }
  // a pole is within the given distance
  else {
    minLat = Math.max(minLat, MIN_LAT);
    maxLat = Math.min(maxLat, MAX_LAT);
    minLon = MIN_LON;
    maxLon = MAX_LON;
  }
  return [
    minLon.radToDeg(),
    minLat.radToDeg(),
    maxLon.radToDeg(),
    maxLat.radToDeg()
  ];
};
Asalisbury
fonte
Este código não funciona de forma alguma. Quer dizer, mesmo depois de consertar os erros óbvios, como minLon = void 0;e maxLon = MAX_LON;ele ainda não funciona.
aroth
1
@aroth, acabei de testar e não tive problemas. Lembre-se de que o centerPointargumento é uma matriz que consiste em duas coordenadas. Por exemplo, getBoundingBox([42.2, 34.5], 50)- void 0é a saída do CoffeeScript para "indefinido" e não afetará a capacidade de execução dos códigos.
asalisbury
este código não funciona. degLat.degToRadnão é uma função
user299709
O código funcionava no Node e no Chrome no estado em que se encontrava, até que eu o coloquei dentro de um projeto no qual estou trabalhando e comecei a receber degToRaderros "não é uma função". Nunca descobri o porquê, mas Number.prototype.não é uma boa ideia para uma função de utilidade como essa, então eu as converti em funções locais normais. Também é importante observar que a caixa retornada é [LNG, LAT, LNG, LAT] em vez de [LAT, LNG, LAT, LNG]. Modifiquei a função return quando usei isso para evitar confusão.
KernelDeimos
9

Como eu precisava de uma estimativa aproximada, para filtrar alguns documentos desnecessários em uma consulta de pesquisa elástica, empreguei a fórmula abaixo:

Min.lat = Given.Lat - (0.009 x N)
Max.lat = Given.Lat + (0.009 x N)
Min.lon = Given.lon - (0.009 x N)
Max.lon = Given.lon + (0.009 x N)

N = kms necessários do local fornecido. Para o seu caso N = 10

Não é preciso, mas prático.

Ajay
fonte
Na verdade, não é preciso, mas ainda é útil e muito fácil de implementar.
MV.
6

Você está procurando uma fórmula elipsóide.

O melhor lugar que encontrei para começar a codificar é baseado na biblioteca Geo :: Ellipsoid do CPAN. Ele fornece uma linha de base para criar seus testes e comparar seus resultados com os resultados. Usei-o como base para uma biblioteca semelhante para PHP em meu empregador anterior.

Geo :: Elipsóide

Dê uma olhada no locationmétodo. Ligue duas vezes e você tem sua bbox.

Você não postou o idioma que estava usando. Pode já haver uma biblioteca de geocodificação disponível para você.

Ah, e se você ainda não percebeu, o Google Maps usa o elipsóide WGS84.

Jcoby
fonte
5

Ilustração da explicação excelente de @Jan Philip Matuschek. (Vote a favor da resposta dele, não esta; estou adicionando isso porque demorei um pouco para entender a resposta original)

A técnica da caixa delimitadora para otimizar a localização dos vizinhos mais próximos precisaria derivar os pares de latitude e longitude mínimo e máximo para um ponto P à distância d. Todos os pontos fora deles estão definitivamente a uma distância maior que d do ponto. Uma coisa a se notar aqui é o cálculo da latitude de interseção, conforme destacado na explicação de Jan Philip Matuschek. A latitude de interseção não está na latitude do ponto P, mas ligeiramente deslocada dele. Esta é uma parte frequentemente perdida, mas importante na determinação da longitude limite mínima e máxima correta para o ponto P para a distância d. Isso também é útil na verificação.

A distância haversine entre (latitude de intersecção, longitude alta) e (latitude, longitude) de P é igual à distância d.

Python gist aqui https://gist.github.com/alexcpn/f95ae83a7ee0293a5225

insira a descrição da imagem aqui

Alex Punnen
fonte
5

Aqui está uma implementação simples usando javascript que é baseada na conversão do grau de latitude para kms where 1 degree latitude ~ 111.2 km.

Estou calculando os limites do mapa a partir de uma determinada latitude e longitude com largura de 10 km.

function getBoundsFromLatLng(lat, lng){
     var lat_change = 10/111.2;
     var lon_change = Math.abs(Math.cos(lat*(Math.PI/180)));
     var bounds = { 
         lat_min : lat - lat_change,
         lon_min : lng - lon_change,
         lat_max : lat + lat_change,
         lon_max : lng + lon_change
     };
     return bounds;
}
Noushad
fonte
4

Adaptei um script PHP que descobri para fazer exatamente isso. Você pode usá-lo para encontrar os cantos de uma caixa em torno de um ponto (digamos, 20 km fora). Meu exemplo específico é para a API do Google Maps:

http://www.richardpeacock.com/blog/2011/11/draw-box-around-coordinate-google-maps-based-miles-or-kilometers

Richard
fonte
-1 O que o OP está procurando é: dado um ponto de referência (lat, lon) e uma distância, encontre a menor caixa de forma que todos os pontos que estão <= "distância" de distância do ponto de referência não estejam fora da caixa. Sua caixa tem seus cantos "distantes" do ponto de referência e, portanto, é muito pequena. Exemplo: o ponto que está "distância" ao norte está bem fora de sua caixa.
John Machin
Bem, por acaso, é exatamente o que eu precisava. Obrigado, mesmo que não responda a esta pergunta :)
Simon Steinberger
Bem, fico feliz que tenha ajudado alguém!
Richard
1

Eu estava trabalhando no problema da caixa delimitadora como uma questão secundária para encontrar todos os pontos dentro do raio SrcRad de um ponto LAT, LONG estático. Existem alguns cálculos que usam

maxLon = $lon + rad2deg($rad/$R/cos(deg2rad($lat)));
minLon = $lon - rad2deg($rad/$R/cos(deg2rad($lat)));

para calcular os limites de longitude, mas descobri que isso não dá todas as respostas necessárias. Porque o que você realmente quer fazer é

(SrcRad/RadEarth)/cos(deg2rad(lat))

Eu sei, eu sei que a resposta deveria ser a mesma, mas descobri que não era. Parecia que por não ter certeza de que estava fazendo o (SRCrad / RadEarth) primeiro e depois dividindo pela parte Cos, eu estava deixando de fora alguns pontos de localização.

Depois de obter todos os pontos da caixa delimitadora, se você tiver uma função que calcula a distância ponto a ponto dada lat, é fácil obter apenas os pontos que estão a um determinado raio de distância do ponto fixo. Aqui está o que eu fiz. Eu sei que foram necessários alguns passos extras, mas me ajudaram

-- GLOBAL Constants
gc_pi CONSTANT REAL := 3.14159265359;  -- Pi

-- Conversion Factor Constants
gc_rad_to_degs          CONSTANT NUMBER := 180/gc_pi; -- Conversion for Radians to Degrees 180/pi
gc_deg_to_rads          CONSTANT NUMBER := gc_pi/180; --Conversion of Degrees to Radians

lv_stat_lat    -- The static latitude point that I am searching from 
lv_stat_long   -- The static longitude point that I am searching from 

-- Angular radius ratio in radians
lv_ang_radius := lv_search_radius / lv_earth_radius;
lv_bb_maxlat := lv_stat_lat + (gc_rad_to_deg * lv_ang_radius);
lv_bb_minlat := lv_stat_lat - (gc_rad_to_deg * lv_ang_radius);

--Here's the tricky part, accounting for the Longitude getting smaller as we move up the latitiude scale
-- I seperated the parts of the equation to make it easier to debug and understand
-- I may not be a smart man but I know what the right answer is... :-)

lv_int_calc := gc_deg_to_rads * lv_stat_lat;
lv_int_calc := COS(lv_int_calc);
lv_int_calc := lv_ang_radius/lv_int_calc;
lv_int_calc := gc_rad_to_degs*lv_int_calc;

lv_bb_maxlong := lv_stat_long + lv_int_calc;
lv_bb_minlong := lv_stat_long - lv_int_calc;

-- Now select the values from your location datatable 
SELECT *  FROM (
SELECT cityaliasname, city, state, zipcode, latitude, longitude, 
-- The actual distance in miles
spherecos_pnttopntdist(lv_stat_lat, lv_stat_long, latitude, longitude, 'M') as miles_dist    
FROM Location_Table 
WHERE latitude between lv_bb_minlat AND lv_bb_maxlat
AND   longitude between lv_bb_minlong and lv_bb_maxlong)
WHERE miles_dist <= lv_limit_distance_miles
order by miles_dist
;
Greg Beck
fonte
0

É muito simples ir para o site panoramio e, em seguida, abrir o mapa do mundo no site panoramio. Em seguida, vá para o local especificado, que exige latitude e longitude.

Então você encontrou latitude e longitude na barra de endereço, por exemplo, neste endereço.

http://www.panoramio.com/map#lt=32.739485&ln=70.491211&z=9&k=1&a=1&tab=1&pl=all

lt = 32,739485 => latitude ln = 70,491211 => longitude

este widget da API JavaScript Panoramio cria uma caixa delimitadora em torno de um par lat / long e, em seguida, retorna todas as fotos com esses limites.

Outro tipo de widget da API JavaScript Panoramio em que você também pode alterar a cor de fundo com exemplo e código está aqui .

Não aparece no modo de composição. Mostra após a publicação.

<div dir="ltr" style="text-align: center;" trbidi="on">
<script src="https://ssl.panoramio.com/wapi/wapi.js?v=1&amp;hl=en"></script>
<div id="wapiblock" style="float: right; margin: 10px 15px"></div>
<script type="text/javascript">
var myRequest = {
  'tag': 'kahna',
  'rect': {'sw': {'lat': -30, 'lng': 10.5}, 'ne': {'lat': 50.5, 'lng': 30}}
};
  var myOptions = {
  'width': 300,
  'height': 200
};
var wapiblock = document.getElementById('wapiblock');
var photo_widget = new panoramio.PhotoWidget('wapiblock', myRequest, myOptions);
photo_widget.setPosition(0);
</script>
</div>
Kahna9
fonte
0

Aqui, converti a resposta de Federico A. Ramponi para PHP, se alguém estiver interessado:

<?php
# deg2rad and rad2deg are already within PHP

# Semi-axes of WGS-84 geoidal reference
$WGS84_a = 6378137.0;  # Major semiaxis [m]
$WGS84_b = 6356752.3;  # Minor semiaxis [m]

# Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
function WGS84EarthRadius($lat)
{
    global $WGS84_a, $WGS84_b;

    $an = $WGS84_a * $WGS84_a * cos($lat);
    $bn = $WGS84_b * $WGS84_b * sin($lat);
    $ad = $WGS84_a * cos($lat);
    $bd = $WGS84_b * sin($lat);

    return sqrt(($an*$an + $bn*$bn)/($ad*$ad + $bd*$bd));
}

# Bounding box surrounding the point at given coordinates,
# assuming local approximation of Earth surface as a sphere
# of radius given by WGS84
function boundingBox($latitudeInDegrees, $longitudeInDegrees, $halfSideInKm)
{
    $lat = deg2rad($latitudeInDegrees);
    $lon = deg2rad($longitudeInDegrees);
    $halfSide = 1000 * $halfSideInKm;

    # Radius of Earth at given latitude
    $radius = WGS84EarthRadius($lat);
    # Radius of the parallel at given latitude
    $pradius = $radius*cos($lat);

    $latMin = $lat - $halfSide / $radius;
    $latMax = $lat + $halfSide / $radius;
    $lonMin = $lon - $halfSide / $pradius;
    $lonMax = $lon + $halfSide / $pradius;

    return array(rad2deg($latMin), rad2deg($lonMin), rad2deg($latMax), rad2deg($lonMax));
}
?>
Joe Black
fonte
0

Obrigado @Fedrico A. pela implementação do Phyton, eu o transferi para uma classe de categoria Objective C. Aqui está:

#import "LocationService+Bounds.h"

//Semi-axes of WGS-84 geoidal reference
const double WGS84_a = 6378137.0; //Major semiaxis [m]
const double WGS84_b = 6356752.3; //Minor semiaxis [m]

@implementation LocationService (Bounds)

struct BoundsLocation {
    double maxLatitude;
    double minLatitude;
    double maxLongitude;
    double minLongitude;
};

+ (struct BoundsLocation)locationBoundsWithLatitude:(double)aLatitude longitude:(double)aLongitude maxDistanceKm:(NSInteger)aMaxKmDistance {
    return [self boundingBoxWithLatitude:aLatitude longitude:aLongitude halfDistanceKm:aMaxKmDistance/2];
}

#pragma mark - Algorithm 

+ (struct BoundsLocation)boundingBoxWithLatitude:(double)aLatitude longitude:(double)aLongitude halfDistanceKm:(double)aDistanceKm {
    double radianLatitude = [self degreesToRadians:aLatitude];
    double radianLongitude = [self degreesToRadians:aLongitude];
    double halfDistanceMeters = aDistanceKm*1000;


    double earthRadius = [self earthRadiusAtLatitude:radianLatitude];
    double parallelRadius = earthRadius*cosl(radianLatitude);

    double radianMinLatitude = radianLatitude - halfDistanceMeters/earthRadius;
    double radianMaxLatitude = radianLatitude + halfDistanceMeters/earthRadius;
    double radianMinLongitude = radianLongitude - halfDistanceMeters/parallelRadius;
    double radianMaxLongitude = radianLongitude + halfDistanceMeters/parallelRadius;

    struct BoundsLocation bounds;
    bounds.minLatitude = [self radiansToDegrees:radianMinLatitude];
    bounds.maxLatitude = [self radiansToDegrees:radianMaxLatitude];
    bounds.minLongitude = [self radiansToDegrees:radianMinLongitude];
    bounds.maxLongitude = [self radiansToDegrees:radianMaxLongitude];

    return bounds;
}

+ (double)earthRadiusAtLatitude:(double)aRadianLatitude {
    double An = WGS84_a * WGS84_a * cosl(aRadianLatitude);
    double Bn = WGS84_b * WGS84_b * sinl(aRadianLatitude);
    double Ad = WGS84_a * cosl(aRadianLatitude);
    double Bd = WGS84_b * sinl(aRadianLatitude);
    return sqrtl( ((An * An) + (Bn * Bn))/((Ad * Ad) + (Bd * Bd)) );
}

+ (double)degreesToRadians:(double)aDegrees {
    return M_PI*aDegrees/180.0;
}

+ (double)radiansToDegrees:(double)aRadians {
    return 180.0*aRadians/M_PI;
}



@end

Eu testei e parece estar funcionando bem. Struct BoundsLocation deve ser substituído por uma classe, usei-a apenas para compartilhá-la aqui.

Jesuslg123
fonte
0

Todas as respostas acima estão apenas parcialmente corretas . Especialmente em regiões como a Austrália, eles sempre incluem a pole e calculam um retângulo muito grande mesmo para 10kms.

Especialmente o algoritmo de Jan Philip Matuschek em http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex incluía um retângulo muito grande de (-37, -90, -180, 180) para quase todos os pontos na Austrália. Isso atinge um grande número de usuários no banco de dados e a distância deve ser calculada para todos os usuários em quase metade do país.

Eu descobri que o Drupal API Earth Algorithm do Rochester Institute of Technology funciona melhor em torno do pólo, bem como em outros lugares, e é muito mais fácil de implementar.

https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54

Use earth_latitude_rangee earth_longitude_rangedo algoritmo acima para calcular o retângulo delimitador

E use a fórmula de cálculo de distância documentada pelo google maps para calcular a distância

https://developers.google.com/maps/solutions/store-locator/clothing-store-locator#outputting-data-as-xml-using-php

Para pesquisar por quilômetros em vez de milhas, substitua 3959 por 6371. Para (Lat, Lng) = (37, -122) e uma tabela de Marcadores com colunas lat e lng , a fórmula é:

SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;

Leia minha resposta detalhada em https://stackoverflow.com/a/45950426/5076414

Sacky San
fonte
0

Aqui está a resposta de Federico Ramponi em Go. Nota: sem verificação de erros :(

import (
    "math"
)

// Semi-axes of WGS-84 geoidal reference
const (
    // Major semiaxis (meters)
    WGS84A = 6378137.0
    // Minor semiaxis (meters)
    WGS84B = 6356752.3
)

// BoundingBox represents the geo-polygon that encompasses the given point and radius
type BoundingBox struct {
    LatMin float64
    LatMax float64
    LonMin float64
    LonMax float64
}

// Convert a degree value to radians
func deg2Rad(deg float64) float64 {
    return math.Pi * deg / 180.0
}

// Convert a radian value to degrees
func rad2Deg(rad float64) float64 {
    return 180.0 * rad / math.Pi
}

// Get the Earth's radius in meters at a given latitude based on the WGS84 ellipsoid
func getWgs84EarthRadius(lat float64) float64 {
    an := WGS84A * WGS84A * math.Cos(lat)
    bn := WGS84B * WGS84B * math.Sin(lat)

    ad := WGS84A * math.Cos(lat)
    bd := WGS84B * math.Sin(lat)

    return math.Sqrt((an*an + bn*bn) / (ad*ad + bd*bd))
}

// GetBoundingBox returns a BoundingBox encompassing the given lat/long point and radius
func GetBoundingBox(latDeg float64, longDeg float64, radiusKm float64) BoundingBox {
    lat := deg2Rad(latDeg)
    lon := deg2Rad(longDeg)
    halfSide := 1000 * radiusKm

    // Radius of Earth at given latitude
    radius := getWgs84EarthRadius(lat)

    pradius := radius * math.Cos(lat)

    latMin := lat - halfSide/radius
    latMax := lat + halfSide/radius
    lonMin := lon - halfSide/pradius
    lonMax := lon + halfSide/pradius

    return BoundingBox{
        LatMin: rad2Deg(latMin),
        LatMax: rad2Deg(latMax),
        LonMin: rad2Deg(lonMin),
        LonMax: rad2Deg(lonMax),
    }
}
sma
fonte