200

私は緯度と経度を持っており、その距離が指定された距離よりも長くなった場合、その距離が最も近い緯度と経度を持つデータベースからレコードを取得したいのですが、それを取得しないでください。

テーブル構造:

id
latitude
longitude
place name
city
country
state
zip
sealevel
4

18 に答える 18

247
SELECT latitude, longitude, SQRT(
    POW(69.1 * (latitude - [startlat]), 2) +
    POW(69.1 * ([startlng] - longitude) * COS(latitude / 57.3), 2)) AS distance
FROM TableName HAVING distance < 25 ORDER BY distance;

[ starlat ][startlng]は、距離の測定を開始する位置です。

于 2011-04-05T07:58:22.633 に答える
93

Google のソリューション:

テーブルの作成

MySQL テーブルを作成するときは、緯度と経度の属性に特に注意する必要があります。Google マップの現在のズーム機能では、小数点以下 6 桁の精度のみが必要です。テーブルに必要なストレージ スペースを最小限に抑えるために、lat および lng 属性がサイズ (10,6) の float であることを指定できます。これにより、フィールドには小数点以下 6 桁と小数点以下 4 桁までを格納できます (例: -123.456789 度)。テーブルには、主キーとして機能する id 属性も必要です。

CREATE TABLE `markers` (
  `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  `name` VARCHAR( 60 ) NOT NULL ,
  `address` VARCHAR( 80 ) NOT NULL ,
  `lat` FLOAT( 10, 6 ) NOT NULL ,
  `lng` FLOAT( 10, 6 ) NOT NULL
) ENGINE = MYISAM ;

テーブルへの入力

テーブルを作成したら、データを入力します。以下に示すサンプル データは、米国中に点在する約 180 のピザ屋のデータです。phpMyAdmin では、[インポート] タブを使用して、CSV (カンマ区切り値) を含むさまざまなファイル形式をインポートできます。Microsoft Excel と Google スプレッドシートはどちらも CSV 形式にエクスポートされるため、CSV ファイルのエクスポート/インポートを通じてスプレッドシートから MySQL テーブルにデータを簡単に転送できます。

INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Frankie Johnnie & Luigo Too','939 W El Camino Real, Mountain View, CA','37.386339','-122.085823');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Amici\'s East Coast Pizzeria','790 Castro St, Mountain View, CA','37.38714','-122.083235');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Kapp\'s Pizza Bar & Grill','191 Castro St, Mountain View, CA','37.393885','-122.078916');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Round Table Pizza: Mountain View','570 N Shoreline Blvd, Mountain View, CA','37.402653','-122.079354');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Tony & Alba\'s Pizza & Pasta','619 Escuela Ave, Mountain View, CA','37.394011','-122.095528');
INSERT INTO `markers` (`name`, `address`, `lat`, `lng`) VALUES ('Oregano\'s Wood-Fired Pizza','4546 El Camino Real, Los Altos, CA','37.401724','-122.114646');

MySQL を使用した場所の検索

特定の緯度/経度から特定の半径距離内にあるマーカー テーブル内の場所を検索するには、Haversine 式に基づく SELECT ステートメントを使用できます。Haversine 式は、一般に、球面上の 2 組の座標間の大圏距離を計算するために使用されます。詳細な数学的説明はウィキペディアで提供されており、プログラミングに関連する式の適切な議論は Movable Type のサイトにあります。

以下は、座標 37, -122 から半径 25 マイル以内にある最も近い 20 の場所を見つける SQL ステートメントです。その行の緯度/経度とターゲットの緯度/経度に基づいて距離を計算し、距離の値が 25 未満の行のみを要求し、クエリ全体を距離で並べ替え、結果を 20 に制限します。マイルではなくキロメートルで検索するには、3959 を 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 < 28 
ORDER BY distance LIMIT 0, 20;

これは、28 マイル未満の距離で緯度と経度を見つけることです。

もう 1 つは、28 ~ 29 マイルの距離でそれらを見つけることです。

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 < 29 and distance > 28 
ORDER BY distance LIMIT 0, 20;

https://developers.google.com/maps/articles/phpsqlsearch_v3#creating-the-map

于 2016-03-26T10:30:26.543 に答える
31

これがPHPで実装された私の完全なソリューションです。

このソリューションでは、 http: //www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL に示されている Haversine 式を使用します。

Haversine 式は、極の周りで弱点を経験することに注意してください。この回答は、これを回避するためにvincenty Great Circle Distance 式を実装する方法を示していますが、私の目的には十分であるため、Haversine を使用することにしました。

緯度を DECIMAL(10,8) として、経度を DECIMAL(11,8) として保存しています。うまくいけば、これが役に立ちます!

showClosest.php

<?PHP
/**
 * Use the Haversine Formula to display the 100 closest matches to $origLat, $origLon
 * Only search the MySQL table $tableName for matches within a 10 mile ($dist) radius.
 */
include("./assets/db/db.php"); // Include database connection function
$db = new database(); // Initiate a new MySQL connection
$tableName = "db.table";
$origLat = 42.1365;
$origLon = -71.7559;
$dist = 10; // This is the maximum distance (in miles) away from $origLat, $origLon in which to search
$query = "SELECT name, latitude, longitude, 3956 * 2 * 
          ASIN(SQRT( POWER(SIN(($origLat - latitude)*pi()/180/2),2)
          +COS($origLat*pi()/180 )*COS(latitude*pi()/180)
          *POWER(SIN(($origLon-longitude)*pi()/180/2),2))) 
          as distance FROM $tableName WHERE 
          longitude between ($origLon-$dist/cos(radians($origLat))*69) 
          and ($origLon+$dist/cos(radians($origLat))*69) 
          and latitude between ($origLat-($dist/69)) 
          and ($origLat+($dist/69)) 
          having distance < $dist ORDER BY distance limit 100"; 
$result = mysql_query($query) or die(mysql_error());
while($row = mysql_fetch_assoc($result)) {
    echo $row['name']." > ".$row['distance']."<BR>";
}
mysql_close($db);
?>

./assets/db/db.php

<?PHP
/**
 * Class to initiate a new MySQL connection based on $dbInfo settings found in dbSettings.php
 *
 * @example $db = new database(); // Initiate a new database connection
 * @example mysql_close($db); // close the connection
 */
class database{
    protected $databaseLink;
    function __construct(){
        include "dbSettings.php";
        $this->database = $dbInfo['host'];
        $this->mysql_user = $dbInfo['user'];
        $this->mysql_pass = $dbInfo['pass'];
        $this->openConnection();
        return $this->get_link();
    }
    function openConnection(){
    $this->databaseLink = mysql_connect($this->database, $this->mysql_user, $this->mysql_pass);
    }

    function get_link(){
    return $this->databaseLink;
    }
}
?>

./assets/db/dbSettings.php

<?php
$dbInfo = array(
    'host'      => "localhost",
    'user'      => "root",
    'pass'      => "password"
);
?>

上記の記事「Geo-Distance-Search-with-MySQL」で提案されているように、MySQL ストアド プロシージャを使用することでパフォーマンスを向上できる場合があります。

〜 17,000 の場所のデータベースがあり、クエリの実行時間は 0.054 秒です。

于 2013-12-07T03:00:51.497 に答える
26

あなたが私のように怠け者である場合に備えて、これと SO に関する他の回答を組み合わせたソリューションを次に示します。

set @orig_lat=37.46; 
set @orig_long=-122.25; 
set @bounding_distance=1;

SELECT
*
,((ACOS(SIN(@orig_lat * PI() / 180) * SIN(`lat` * PI() / 180) + COS(@orig_lat * PI() / 180) * COS(`lat` * PI() / 180) * COS((@orig_long - `long`) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS `distance` 
FROM `cities` 
WHERE
(
  `lat` BETWEEN (@orig_lat - @bounding_distance) AND (@orig_lat + @bounding_distance)
  AND `long` BETWEEN (@orig_long - @bounding_distance) AND (@orig_long + @bounding_distance)
)
ORDER BY `distance` ASC
limit 25;
于 2011-05-09T06:25:33.753 に答える
13

簡単なもの;)

SELECT * FROM `WAYPOINTS` W ORDER BY
ABS(ABS(W.`LATITUDE`-53.63) +
ABS(W.`LONGITUDE`-9.9)) ASC LIMIT 30;

座標を必要なものに置き換えるだけです。値は double として格納する必要があります。これは、動作する MySQL 5.x の例です。

乾杯

于 2012-10-14T12:52:36.513 に答える
6

これを試してみてください。指定された座標 (50 km 以内) に最も近いポイントが表示されます。それは完全に機能します:

SELECT m.name,
    m.lat, m.lon,
    p.distance_unit
             * DEGREES(ACOS(COS(RADIANS(p.latpoint))
             * COS(RADIANS(m.lat))
             * COS(RADIANS(p.longpoint) - RADIANS(m.lon))
             + SIN(RADIANS(p.latpoint))
             * SIN(RADIANS(m.lat)))) AS distance_in_km
FROM <table_name> AS m
JOIN (
      SELECT <userLat> AS latpoint, <userLon> AS longpoint,
             50.0 AS radius, 111.045 AS distance_unit
     ) AS p ON 1=1
WHERE m.lat
BETWEEN p.latpoint  - (p.radius / p.distance_unit)
    AND p.latpoint  + (p.radius / p.distance_unit)
    AND m.lon BETWEEN p.longpoint - (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
    AND p.longpoint + (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
ORDER BY distance_in_km

変更するだけ<table_name>です。<userLat><userLon>

このソリューションの詳細については、http ://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/ を参照してください。

于 2015-05-07T16:17:11.047 に答える
5

あなたはhasersine formulaのようなものを探しています。こちらもご覧ください

他にもありますが、これが最も一般的に引用されています。

さらに堅牢なものを探している場合は、データベースの GIS 機能を確認することをお勧めします。ポイント (都市) が特定の多角形 (地域、国、大陸) 内に表示されるかどうかを示すなど、いくつかの優れた機能があります。

于 2010-02-10T03:28:21.477 に答える
4

記事Geo-Distance-Search-with-MySQL に基づいて、このコードを確認してください。

例: 半径 10 マイル内で、現在の場所に最も近い 10 のホテルを検索します。

#Please notice that (lat,lng) values mustn't be negatives to perform all calculations

set @my_lat=34.6087674878572; 
set @my_lng=58.3783670308302;
set @dist=10; #10 miles radius

SELECT dest.id, dest.lat, dest.lng,  3956 * 2 * ASIN(SQRT(POWER(SIN((@my_lat -abs(dest.lat)) * pi()/180 / 2),2) + COS(@my_lat * pi()/180 ) * COS(abs(dest.lat) *  pi()/180) * POWER(SIN((@my_lng - abs(dest.lng)) *  pi()/180 / 2), 2))
) as distance
FROM hotel as dest
having distance < @dist
ORDER BY distance limit 10;

#Also notice that distance are expressed in terms of radius.
于 2014-10-16T14:49:59.043 に答える
4

私に最も近いユーザーを見つける:

メートル単位の距離

Vincenty の式に基づく

私はユーザーテーブルを持っています:

+----+-----------------------+---------+--------------+---------------+
| id | email                 | name    | location_lat | location_long |
+----+-----------------------+---------+--------------+---------------+
| 13 | xxxxxx@xxxxxxxxxx.com | Isaac   | 17.2675625   | -97.6802361   |
| 14 | xxxx@xxxxxxx.com.mx   | Monse   | 19.392702    | -99.172596    |
+----+-----------------------+---------+--------------+---------------+

SQL:

-- my location:  lat   19.391124   -99.165660
SELECT 
(ATAN(
    SQRT(
        POW(COS(RADIANS(users.location_lat)) * SIN(RADIANS(users.location_long) - RADIANS(-99.165660)), 2) +
        POW(COS(RADIANS(19.391124)) * SIN(RADIANS(users.location_lat)) - 
       SIN(RADIANS(19.391124)) * cos(RADIANS(users.location_lat)) * cos(RADIANS(users.location_long) - RADIANS(-99.165660)), 2)
    )
    ,
    SIN(RADIANS(19.391124)) * 
    SIN(RADIANS(users.location_lat)) + 
    COS(RADIANS(19.391124)) * 
    COS(RADIANS(users.location_lat)) * 
    COS(RADIANS(users.location_long) - RADIANS(-99.165660))
 ) * 6371000) as distance,
users.id
FROM users
ORDER BY distance ASC

地球の半径 : 6371000 (メートル)

于 2016-08-23T23:22:15.833 に答える
2

距離に制限のある最近傍検索を実行したいようです。私が知る限り、SQL はこのようなものをサポートしておらず、R-treekd-treeなどの代替データ構造を使用する必要があります。

于 2010-02-10T03:29:22.800 に答える
0

PostGIS、SpatialLite、SQLServer2008、またはOracleSpatialを使用する必要があるようです。彼らは皆、空間SQLを使ってこの質問に答えることができます。

于 2010-02-10T04:24:45.673 に答える
-20

この問題はそれほど難しいものではありませんが、最適化する必要がある場合はさらに複雑になります。

つまり、データベースに100の場所がありますか、それとも1億の場所がありますか?それは大きな違いを生みます。

場所の数が少ない場合は、->を実行するだけで、SQLからコードに移動できます。

Select * from Location

それらをコードに組み込んだら、Haversineの式を使用して、各緯度/経度とオリジナルの間の距離を計算し、並べ替えます。

于 2010-02-10T04:11:40.940 に答える