Frage

Ich habe eine funktionierende PHP-Skript bekam, die Werte Länge und Breite bekommt und dann gibt sie in eine MySQL-Abfrage. Ich mag es nur MySQL machen. Hier ist mein aktueller PHP-Code:

if ($distance != "Any" && $customer_zip != "") { //get the great circle distance

    //get the origin zip code info
    $zip_sql = "SELECT * FROM zip_code WHERE zip_code = '$customer_zip'";
    $result = mysql_query($zip_sql);
    $row = mysql_fetch_array($result);
    $origin_lat = $row['lat'];
    $origin_lon = $row['lon'];

    //get the range
    $lat_range = $distance/69.172;
    $lon_range = abs($distance/(cos($details[0]) * 69.172));
    $min_lat = number_format($origin_lat - $lat_range, "4", ".", "");
    $max_lat = number_format($origin_lat + $lat_range, "4", ".", "");
    $min_lon = number_format($origin_lon - $lon_range, "4", ".", "");
    $max_lon = number_format($origin_lon + $lon_range, "4", ".", "");
    $sql .= "lat BETWEEN '$min_lat' AND '$max_lat' AND lon BETWEEN '$min_lon' AND '$max_lon' AND ";
    }

Wer weiß, wie diese völlig MySQL zu machen? Ich habe das Internet ein wenig gestöbert, aber die meisten der Literatur ist es ziemlich verwirrend.

War es hilfreich?

Lösung

Google Code FAQ - Erstellen eines Store Locator mit PHP, MySQL und Google Maps :

  

Hier ist die SQL-Anweisung, die die nächsten 20 Standorte finden, die auf die 37 in einem Umkreis von 25 Meilen sind, -122 koordinieren. Es berechnet die Entfernung auf der Grundlage der Breite / Länge dieser Zeile und die Ziel Breite / Länge, und fragt dann nur Zeilen, in denen der Abstand Wert kleiner als 25 ist, ordnet die gesamte Abfrage nach Entfernung und begrenzt auf 20 Ergebnisse. Um von Kilometern zu suchen statt Meilen, ersetzen 3959 mit 6371.

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;

Andere Tipps

$greatCircleDistance = acos( cos($latitude0) * cos($latitude1) * cos($longitude0 - $longitude1) + sin($latitude0) * sin($latitude1));

mit der geographischen Breite und Länge in Radiant.

so

SELECT 
  acos( 
      cos(radians( $latitude0 ))
    * cos(radians( $latitude1 ))
    * cos(radians( $longitude0 ) - radians( $longitude1 ))
    + sin(radians( $latitude0 )) 
    * sin(radians( $latitude1 ))
  ) AS greatCircleDistance 
 FROM yourTable;

Ihre SQL-Abfrage

Ihre Ergebnisse in km oder Meilen erhalten, multiplizieren Sie das Ergebnis mit dem mittleren Radius der Erde (3959 Meilen, 6371 Km oder 3440 nautische Meilen)

Das, was Sie in Ihrem Beispiel sind die Berechnung ist ein Begrenzungsrahmen. Wenn Sie legen Sie Ihre Daten Koordinate in einem räumlichen aktiviert MySQL Spalte können Sie MySQL-Build-in-Funktionalität nutzen die Daten abgefragt werden.

SELECT 
  id
FROM spatialEnabledTable
WHERE 
  MBRWithin(ogc_point, GeomFromText('Polygon((0 0,0 3,3 3,3 0,0 0))'))

Wenn Sie Helfer Felder in der Koordinaten-Tabelle hinzufügen, können Sie Antwortzeit der Abfrage verbessern.

Wie folgt aus:

CREATE TABLE `Coordinates` (
`id` INT(10) UNSIGNED NOT NULL COMMENT 'id for the object',
`type` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'type',
`sin_lat` FLOAT NOT NULL COMMENT 'sin(lat) in radians',
`cos_cos` FLOAT NOT NULL COMMENT 'cos(lat)*cos(lon) in radians',
`cos_sin` FLOAT NOT NULL COMMENT 'cos(lat)*sin(lon) in radians',
`lat` FLOAT NOT NULL COMMENT 'latitude in degrees',
`lon` FLOAT NOT NULL COMMENT 'longitude in degrees',
INDEX `lat_lon_idx` (`lat`, `lon`)
)    

Wenn Sie TokuDB verwenden, werden Sie noch bessere Leistung, wenn Sie Clustering hinzufügen Indizes auf eine der beiden Prädikate zum Beispiel wie folgt aus:

alter table Coordinates add clustering index c_lat(lat);
alter table Coordinates add clustering index c_lon(lon);

Sie müssen den Grund lat und lon in Grad sowie sin (lat) in Radianten, cos (lat) * cos (lon) in Radianten und cos (lat) * sin (lon) in Radian für jeden Punkt . Dann sind Sie eine MySQL-Funktion erstellen, smth wie folgt aus:

CREATE FUNCTION `geodistance`(`sin_lat1` FLOAT,
                              `cos_cos1` FLOAT, `cos_sin1` FLOAT,
                              `sin_lat2` FLOAT,
                              `cos_cos2` FLOAT, `cos_sin2` FLOAT)
    RETURNS float
    LANGUAGE SQL
    DETERMINISTIC
    CONTAINS SQL
    SQL SECURITY INVOKER
   BEGIN
   RETURN acos(sin_lat1*sin_lat2 + cos_cos1*cos_cos2 + cos_sin1*cos_sin2);
   END

Damit haben Sie den Abstand.

Vergessen Sie nicht, einen Index auf lat / lon hinzufügen, damit die Begrenzungsbox die Suche, anstatt es zu verlangsamen helfen können (der Index bereits in der CREATE TABLE-Abfrage oben hinzugefügt wird).

INDEX `lat_lon_idx` (`lat`, `lon`)

eine alte Tabelle mit nur lat / lon Koordinaten angegeben, können Sie ein Skript einrichten, um es so zu aktualisieren: (php meekrodb)

$users = DB::query('SELECT id,lat,lon FROM Old_Coordinates');

foreach ($users as $user)
{
  $lat_rad = deg2rad($user['lat']);
  $lon_rad = deg2rad($user['lon']);

  DB::replace('Coordinates', array(
    'object_id' => $user['id'],
    'object_type' => 0,
    'sin_lat' => sin($lat_rad),
    'cos_cos' => cos($lat_rad)*cos($lon_rad),
    'cos_sin' => cos($lat_rad)*sin($lon_rad),
    'lat' => $user['lat'],
    'lon' => $user['lon']
  ));
}

Dann optimieren Sie die aktuelle Abfrage nur die Entfernung Berechnung tun, wenn es wirklich benötigt wird, zum Beispiel durch den Kreis begrenzt (na ja, oval) von innen und außen. Dafür müssen Sie mehrere Metriken für die Abfrage selbst vorauszuberechnen:

// assuming the search center coordinates are $lat and $lon in degrees
// and radius in km is given in $distance
$lat_rad = deg2rad($lat);
$lon_rad = deg2rad($lon);
$R = 6371; // earth's radius, km
$distance_rad = $distance/$R;
$distance_rad_plus = $distance_rad * 1.06; // ovality error for outer bounding box
$dist_deg_lat = rad2deg($distance_rad_plus); //outer bounding box
$dist_deg_lon = rad2deg($distance_rad_plus/cos(deg2rad($lat)));
$dist_deg_lat_small = rad2deg($distance_rad/sqrt(2)); //inner bounding box
$dist_deg_lon_small = rad2deg($distance_rad/cos(deg2rad($lat))/sqrt(2));

solche Zubereitungen, geht die Abfrage so etwas wie dieser (php):

$neighbors = DB::query("SELECT id, type, lat, lon,
       geodistance(sin_lat,cos_cos,cos_sin,%d,%d,%d) as distance
       FROM Coordinates WHERE
       lat BETWEEN %d AND %d AND lon BETWEEN %d AND %d
       HAVING (lat BETWEEN %d AND %d AND lon BETWEEN %d AND %d) OR distance <= %d",
  // center radian values: sin_lat, cos_cos, cos_sin
       sin($lat_rad),cos($lat_rad)*cos($lon_rad),cos($lat_rad)*sin($lon_rad),
  // min_lat, max_lat, min_lon, max_lon for the outside box
       $lat-$dist_deg_lat,$lat+$dist_deg_lat,
       $lon-$dist_deg_lon,$lon+$dist_deg_lon,
  // min_lat, max_lat, min_lon, max_lon for the inside box
       $lat-$dist_deg_lat_small,$lat+$dist_deg_lat_small,
       $lon-$dist_deg_lon_small,$lon+$dist_deg_lon_small,
  // distance in radians
       $distance_rad);

auf der obigen Abfrage erklären könnte sagen, dass es mit Index nicht, es sei denn es genügend Ergebnisse ist so auszulösen. Der Index wird verwendet, wenn es in der Koordinaten-Tabelle ausreichend Daten vorliegen. Du kannst hinzufügen     FORCE INDEX (lat_lon_idx) auf die SELECT, um den Index ohne Bezug auf die Tischgröße verwenden zu machen, so dass Sie mit überprüfen können erklären, dass es richtig funktioniert.

Mit den obigen Codebeispielen sollten Sie eine Arbeits haben und skalierbare Implementierung von Objektsuche nach Entfernung mit minimalem Fehler.

Ich habe diese im Detail zu arbeiten, so dass ich mein Ergebnis teilen. Dieser verwendet eine zip Tabelle mit latitude und longitude Tabellen. Es hängt nicht von Google Maps; enthält lat / long eher können Sie es an jedem Tisch anpassen.

SELECT zip, primary_city, 
       latitude, longitude, distance_in_mi
  FROM (
SELECT zip, primary_city, latitude, longitude,r,
       (3963.17 * ACOS(COS(RADIANS(latpoint)) 
                 * COS(RADIANS(latitude)) 
                 * COS(RADIANS(longpoint) - RADIANS(longitude)) 
                 + SIN(RADIANS(latpoint)) 
                 * SIN(RADIANS(latitude)))) AS distance_in_mi
 FROM zip
 JOIN (
        SELECT  42.81  AS latpoint,  -70.81 AS longpoint, 50.0 AS r
   ) AS p 
 WHERE latitude  
  BETWEEN latpoint  - (r / 69) 
      AND latpoint  + (r / 69)
   AND longitude 
  BETWEEN longpoint - (r / (69 * COS(RADIANS(latpoint))))
      AND longpoint + (r / (69 * COS(RADIANS(latpoint))))
  ) d
 WHERE distance_in_mi <= r
 ORDER BY distance_in_mi
 LIMIT 30

Schauen Sie sich diese Linie in der Mitte dieser Abfrage:

    SELECT  42.81  AS latpoint,  -70.81 AS longpoint, 50.0 AS r

Dies sucht nach den 30 nächsten Einträge in der Tabelle zip innerhalb 50,0 Meilen von der lat / long Punkt 42.81 / -70,81. Wenn Sie dies in eine App bauen, das ist, wo Sie Ihren eigenen Punkt und Suchradius setzen.

Wenn Sie lieber in Kilometer arbeiten, als Meilen, ändern 69 111.045 3963.17 und ändert in der Abfrage 6378.10.

Hier ist eine detaillierte writeup. Ich hoffe, dass es jemand hilft. http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

Ich habe eine Prozedur geschrieben, die das gleiche berechnen können, aber Sie haben die Breite und Länge in der jeweiligen Tabelle eingeben.

drop procedure if exists select_lattitude_longitude;

delimiter //

create procedure select_lattitude_longitude(In CityName1 varchar(20) , In CityName2 varchar(20))

begin

    declare origin_lat float(10,2);
    declare origin_long float(10,2);

    declare dest_lat float(10,2);
    declare dest_long float(10,2);

    if CityName1  Not In (select Name from City_lat_lon) OR CityName2  Not In (select Name from City_lat_lon) then 

        select 'The Name Not Exist or Not Valid Please Check the Names given by you' as Message;

    else

        select lattitude into  origin_lat from City_lat_lon where Name=CityName1;

        select longitude into  origin_long  from City_lat_lon where Name=CityName1;

        select lattitude into  dest_lat from City_lat_lon where Name=CityName2;

        select longitude into  dest_long  from City_lat_lon where Name=CityName2;

        select origin_lat as CityName1_lattitude,
               origin_long as CityName1_longitude,
               dest_lat as CityName2_lattitude,
               dest_long as CityName2_longitude;

        SELECT 3956 * 2 * ASIN(SQRT( POWER(SIN((origin_lat - dest_lat) * pi()/180 / 2), 2) + COS(origin_lat * pi()/180) * COS(dest_lat * pi()/180) * POWER(SIN((origin_long-dest_long) * pi()/180 / 2), 2) )) * 1.609344 as Distance_In_Kms ;

    end if;

end ;

//

delimiter ;

Ich kann nicht auf die obige Antwort kommentieren, aber mit @Pavel Chuchuva Antwort vorsichtig sein. Diese Formel wird kein Ergebnis zurück, wenn beide Koordinaten gleich sind. In diesem Fall Abstand null ist, und so wird die Zeile nicht mit dieser Formel zurückgegeben werden, wie es ist.

Ich bin kein MySQL-Experte, aber dies scheint für mich zu arbeiten:

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 OR distance IS NULL ORDER BY distance LIMIT 0 , 20;
 SELECT *, (  
    6371 * acos(cos(radians(search_lat)) * cos(radians(lat) ) *   
cos(radians(lng) - radians(search_lng)) + sin(radians(search_lat)) *         sin(radians(lat)))  
) AS distance  
FROM table  
WHERE lat != search_lat AND lng != search_lng AND distance < 25  
 ORDER BY distance  
FETCH 10 ONLY 

für Entfernung von 25 km

dachte, ich meine JavaScript-Implementierung eine gute Referenz sein würde:

/*
 * Check to see if the second coord is within the precision ( meters )
 * of the first coord and return accordingly
 */
function checkWithinBound(coord_one, coord_two, precision) {
    var distance = 3959000 * Math.acos( 
        Math.cos( degree_to_radian( coord_two.lat ) ) * 
        Math.cos( degree_to_radian( coord_one.lat ) ) * 
        Math.cos( 
            degree_to_radian( coord_one.lng ) - degree_to_radian( coord_two.lng ) 
        ) +
        Math.sin( degree_to_radian( coord_two.lat ) ) * 
        Math.sin( degree_to_radian( coord_one.lat ) ) 
    );
    return distance <= precision;
}

/**
 * Get radian from given degree
 */
function degree_to_radian(degree) {
    return degree * (Math.PI / 180);
}

berechnen Abstand in Mysql

 SELECT (6371 * acos(cos(radians(lat2)) * cos(radians(lat1) ) * cos(radians(long1) -radians(long2)) + sin(radians(lat2)) * sin(radians(lat1)))) AS distance

also Wert Abstand berechnet werden und jeder kann je nach Bedarf anwenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top