Pregunta

Esto puede o no estar claro, déjame un comentario si no estoy en la base, o si necesitas más información. Quizás ya exista una solución para lo que quiero en PHP.

Estoy buscando una función que sume o reste una distancia de un valor de longitud O latitud.

Motivo: tengo una base de datos con todas las latitudes y longitudes y quiero formar una consulta para extraer todas las ciudades dentro de X kilómetros (o millas). Mi consulta se vería así ...

Select * From Cities Where (Longitude > X1 and Longitude < X2) And (Latitude > Y1 and Latitude < Y2)

 Where X1 = Longitude - (distance)
 Where X2 = Longitude + (distance)

 Where Y1 = Latitude - (distance)
 Where Y2 = Latitude + (distance)

Estoy trabajando en PHP, con una base de datos MySql.

¡Abierto a cualquier sugerencia también! :)

¿Fue útil?

Solución

Esta es una consulta MySQL que hará exactamente lo que desea. Tenga en cuenta que cosas como esta son aproximaciones en general, ya que la tierra no es perfectamente esférica ni tiene en cuenta montañas, colinas, valles, etc. Utilizamos este código en AcademicHomes.com con PHP y MySQL, devuelve registros dentro de $ radio millas de $ latitud, $ longitud.

$res = mysql_query("SELECT
    * 
FROM
    your_table
WHERE
    (
        (69.1 * (latitude - " . $latitude . ")) * 
        (69.1 * (latitude - " . $latitude . "))
    ) + ( 
        (69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3)) * 
        (69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3))
    ) < " . pow($radius, 2) . " 
ORDER BY 
    (
        (69.1 * (latitude - " . $latitude . ")) * 
        (69.1 * (latitude - " . $latitude . "))
    ) + ( 
        (69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3)) * 
        (69.1 * (longitude - " . $longitude . ") * COS(" . $latitude . " / 57.3))
    ) ASC");

Otros consejos

EDITAR: si tiene, en algún lugar, una lista de todas las ciudades del mundo junto con su lat. y largo. valores, puedes hacer una búsqueda. En este caso, vea mi primer enlace a continuación para obtener la fórmula para calcular el ancho de un grado longitudinal en latitud texto alternativo:

texto alternativo ??

Honestamente, las complicaciones detrás de este problema son tales que sería mucho mejor usar un servicio como Google Maps para obtener sus datos. Específicamente, la Tierra no es una esfera perfecta, y la distancia entre dos grados varía a medida que se acerca o se aleja del ecuador.

Consulte http://en.wikipedia.org/wiki/Geographic_coordinate_system para ver ejemplos de lo que quiero decir, y echa un vistazo a la API de Google Maps .

Dependiendo de cuántas ciudades incluya, puede calcular previamente la lista. Hacemos esto aquí para una aplicación interna donde una imprecisión de + 100m es demasiado para nuestra configuración. Funciona al tener una tabla de dos claves de ubicación1, ubicación2, distancia. Entonces podemos retirar ubicaciones x distancia desde la ubicación1 muy rápidamente.

Además, dado que los cálculos se pueden hacer sin conexión, no afecta el funcionamiento del sistema. Los usuarios también obtienen resultados más rápidos.

Intenté usar el código anterior, y las respuestas fueron demasiado altas cuando la distancia entre los puntos estaba en el rango de 20-30 millas, y estoy de acuerdo con algunas millas de error. Hablamos con un compañero de mapeo mío y se nos ocurrió este. El código es python, pero puedes traducirlo con bastante facilidad. Para evitar la conversión constante a radianes, rehice mi base de datos, convirtiendo los puntos lat / lng de grados a radianes. Lo bueno de esto es que la mayor parte de las matemáticas se realiza principalmente una vez.

ra = 3963.1906 # radius @ equator in miles, change to km  if you want distance in km
rb = 3949.90275  # radius @ poles in miles, change to km  if you want distance in km
ra2 = ra * ra
rb2 = rb * rb

phi = self.lat

big_ol_constant = (math.pow(ra2*math.cos(phi), 2) + pow(rb2*math.sin(phi), 2))/ (pow(ra*math.cos(phi), 2) + pow(rb*math.sin(phi), 2))

sqlWhere = "%(distance)g > sqrt((power(lat - %(lat)g,2) + power(lng-%(lng)g,2)) * %(big_ol_constant)g)" % {
    'big_ol_constant': big_ol_constant, 'lat': self.lat, 'lng': self.lng, 'distance': distance}

# This is the Django portion of it, where the ORM kicks in.  sqlWhere is what you would put after the WHERE part of your SQL Query.
qs = ZipData.objects.extra(where=[sqlWhere]);

Parece ser muy preciso cuando la distancia es pequeña, y dentro de 10 millas más o menos a medida que la distancia crece a 200 millas, (por supuesto, para entonces, tiene problemas con `` cuando el cuervo vuela '' vs `` carreteras pavimentadas '' ;).

Aquí está el modelo ZipData que mencioné anteriormente.

class ZipData(models.Model):
    zipcode = ZipCodeField(null=False, blank=False, verbose_name="ZipCode", primary_key=True)
    city = models.CharField(max_length=32, null=False, blank=False)
    state = models.CharField(max_length=2)
    lat = models.FloatField(null=False, blank=False)
    lng = models.FloatField(null=False, blank=False)

Una nota adicional es que puede obtener MUCHOS datos geográficos relacionados con códigos postales en GeoNames. org e incluso tienen algunas API de servicio web que también puedes usar.

Hay muchas (malas opciones)

  • Calcule la distancia usando la fórmula matemática (trate X1-X2 e Y1-Y2) como vectores.

  • Cree una tabla de búsqueda por adelantado con todas las combinaciones y mantenga las distancias.

  • Considere usar una extensión específica de SIG de MySQL. Aquí está un artículo que encontré sobre esto .

lessthandot.com en realidad tiene 3 formas diferentes de hacer esto. tendrás que desplazarte un poco por los blogs, pero están ahí. http://blogs.lessthandot.com/

La siguiente función es de nerddinner 's (aplicación de ejemplo ASP.NET MVC disponible en la base de datos codeplex ) (MSSQL).

ALTER FUNCTION [dbo].[DistanceBetween] (@Lat1 as real,
                @Long1 as real, @Lat2 as real, @Long2 as real)
RETURNS real
AS
BEGIN

DECLARE @dLat1InRad as float(53);
SET @dLat1InRad = @Lat1 * (PI()/180.0);
DECLARE @dLong1InRad as float(53);
SET @dLong1InRad = @Long1 * (PI()/180.0);
DECLARE @dLat2InRad as float(53);
SET @dLat2InRad = @Lat2 * (PI()/180.0);
DECLARE @dLong2InRad as float(53);
SET @dLong2InRad = @Long2 * (PI()/180.0);

DECLARE @dLongitude as float(53);
SET @dLongitude = @dLong2InRad - @dLong1InRad;
DECLARE @dLatitude as float(53);
SET @dLatitude = @dLat2InRad - @dLat1InRad;
/* Intermediate result a. */
DECLARE @a as float(53);
SET @a = SQUARE (SIN (@dLatitude / 2.0)) + COS (@dLat1InRad)
                 * COS (@dLat2InRad)
                 * SQUARE(SIN (@dLongitude / 2.0));
/* Intermediate result c (great circle distance in Radians). */
DECLARE @c as real;
SET @c = 2.0 * ATN2 (SQRT (@a), SQRT (1.0 - @a));
DECLARE @kEarthRadius as real;
/* SET kEarthRadius = 3956.0 miles */
SET @kEarthRadius = 6376.5;        /* kms */

DECLARE @dDistance as real;
SET @dDistance = @kEarthRadius * @c;
return (@dDistance);
END

Supongo que esto podría ser útil.

Puede usar el teorema de Pitágoras para calcular la proximidad de dos pares de puntos lat / lon.

Si tiene dos ubicaciones (Alfa y Beta), puede calcular su distancia con:

SQRT( POW(Alpha_lat - Beta_lat,2) + POW(Alpha_lon - Beta_lon,2) )

Utilizando la configuración de la siguiente URL, he creado la consulta a continuación. (Tenga en cuenta que estoy usando codeIgnitor para consultar la base de datos)

http: / /howto-use-mysql-spatial-ext.blogspot.com/2007/11/using-circular-area-selection.html

function getRadius($point="POINT(-29.8368 30.9096)", $radius=2)
{
    $km = 0.009;
    $center = "GeomFromText('$point')";
    $radius = $radius*$km;
    $bbox = "CONCAT('POLYGON((',
        X($center) - $radius, ' ', Y($center) - $radius, ',',
        X($center) + $radius, ' ', Y($center) - $radius, ',',
        X($center) + $radius, ' ', Y($center) + $radius, ',',
        X($center) - $radius, ' ', Y($center) + $radius, ',',
        X($center) - $radius, ' ', Y($center) - $radius, '
    ))')";

    $query = $this->db->query("
    SELECT id, AsText(latLng) AS latLng, (SQRT(POW( ABS( X(latLng) - X({$center})), 2) + POW( ABS(Y(latLng) - Y({$center})), 2 )))/0.009 AS distance
    FROM crime_listing
    WHERE Intersects( latLng, GeomFromText($bbox) )
    AND SQRT(POW( ABS( X(latLng) - X({$center})), 2) + POW( ABS(Y(latLng) - Y({$center})), 2 )) < $radius
    ORDER BY distance
        ");

    if($query->num_rows()>0){
        return($query->result());
    }else{
        return false;
    }
}

No reinventes la rueda. Esta es una consulta espacial. Utilice MySQL's extensiones espaciales integradas para almacenar los datos de coordenadas de latitud y longitud en tipo de columna de geometría MySQL nativa . Luego use Distancia para consultar los puntos que se encuentran a una distancia específica entre sí.

Descargo de responsabilidad: esto se basa en leer la documentación, no lo he intentado yo mismo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top