Tôi có vĩ độ và kinh độ và tôi muốn lấy bản ghi từ cơ sở dữ liệu, có vĩ độ và kinh độ gần nhất theo khoảng cách, nếu khoảng cách đó dài hơn chỉ định, thì đừng lấy lại.
Cấu trúc bảng:
id
latitude
longitude
place name
city
country
state
zip
sealevel
Tôi có vĩ độ và kinh độ và tôi muốn lấy bản ghi từ cơ sở dữ liệu, có vĩ độ và kinh độ gần nhất theo khoảng cách, nếu khoảng cách đó dài hơn chỉ định, thì đừng lấy lại.
Cấu trúc bảng:
id
latitude
longitude
place name
city
country
state
zip
sealevel
Câu trả lời:
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;
trong đó [starlat] và [startlng] là vị trí bắt đầu đo khoảng cách.
Khi bạn tạo bảng MySQL, bạn muốn đặc biệt chú ý đến các thuộc tính lat và lng. Với khả năng thu phóng hiện tại của Google Maps, bạn chỉ cần 6 chữ số chính xác sau số thập phân. Để giữ không gian lưu trữ cần thiết cho bảng của bạn ở mức tối thiểu, bạn có thể chỉ định rằng các thuộc tính lat và lng có kích thước nổi (10,6). Điều đó sẽ cho phép các trường lưu trữ 6 chữ số sau số thập phân, cộng với tối đa 4 chữ số trước số thập phân, ví dụ -123,456789 độ. Bảng của bạn cũng phải có một thuộc tính id để dùng làm khóa chính.
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 ;
Sau khi tạo bảng, đã đến lúc điền dữ liệu vào bảng. Dữ liệu mẫu được cung cấp dưới đây là cho khoảng 180 pizzarias rải rác trên khắp Hoa Kỳ. Trong phpMyAdmin, bạn có thể sử dụng tab NHẬP để nhập các định dạng tệp khác nhau, bao gồm CSV (giá trị được phân tách bằng dấu phẩy). Cả Microsoft Excel và Google Bảng tính đều xuất sang định dạng CSV, do đó bạn có thể dễ dàng chuyển dữ liệu từ bảng tính sang bảng MySQL thông qua xuất / nhập tệp CSV.
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');
Để tìm các vị trí trong bảng đánh dấu của bạn nằm trong khoảng cách bán kính nhất định của một vĩ độ / kinh độ nhất định, bạn có thể sử dụng câu lệnh CHỌN dựa trên công thức Haversine. Công thức Haversine thường được sử dụng để tính khoảng cách vòng tròn lớn giữa hai cặp tọa độ trên một mặt cầu. Một lời giải thích sâu về toán học được đưa ra bởi Wikipedia và một cuộc thảo luận tốt về công thức vì nó liên quan đến lập trình trên trang web của Movable Type.
Dưới đây là các câu lệnh SQL mà sẽ tìm ra 20 địa điểm gần nhất mà là trong vòng bán kính 25 dặm về phía 37, -122 phối hợp. Nó tính toán khoảng cách dựa trên vĩ độ / kinh độ của hàng đó và vĩ độ / kinh độ đích, sau đó chỉ yêu cầu các hàng có giá trị khoảng cách nhỏ hơn 25, sắp xếp toàn bộ truy vấn theo khoảng cách và giới hạn ở 20 kết quả. Để tìm kiếm theo km thay vì dặm, thay thế 3959 với 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;
Cái này là để tìm các vĩ độ và kinh độ trong một khoảng cách ít hơn 28 dặm.
Một số khác là để tìm thấy chúng trong một khoảng cách từ 28 đến 29 dặm:
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
HAVING distance < 25
như chúng ta truy vấn địa điểm trong vòng bán kính 25 dặm?
Đây là giải pháp đầy đủ của tôi được thực hiện trong PHP.
Giải pháp này sử dụng công thức Haversine như được trình bày trong http://www.scribed.com/doc/2569355/Geo-Distance-Search-with-MyQuery .
Cần lưu ý rằng công thức Haversine gặp phải điểm yếu xung quanh các cực. Câu trả lời này cho thấy cách triển khai công thức vincenty Great Circle Khoảng cách để giải quyết vấn đề này, tuy nhiên tôi đã chọn chỉ sử dụng Haversine vì nó đủ tốt cho mục đích của tôi.
Tôi đang lưu trữ vĩ độ là DECIMAL (10,8) và kinh độ là DECIMAL (11,8). Hy vọng điều này sẽ giúp!
<?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);
?>
<?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;
}
}
?>
<?php
$dbInfo = array(
'host' => "localhost",
'user' => "root",
'pass' => "password"
);
?>
Có thể tăng hiệu suất bằng cách sử dụng quy trình lưu trữ MySQL như được đề xuất bởi bài viết "Geo-Khoảng cách-Tìm kiếm-với-MySQL" được đăng ở trên.
Tôi có cơ sở dữ liệu ~ 17.000 địa điểm và thời gian thực hiện truy vấn là 0,054 giây.
abs
nên được loại bỏ. Không cần phải lấy giá trị abs khi chuyển đổi từ độ sang radian, và, ngay cả khi bạn đã làm, bạn đang làm điều đó chỉ là một trong những vĩ độ. Vui lòng chỉnh sửa nó để sửa lỗi.
69
bằng 111,044736
(quên rằng trong nhận xét trên)
Chỉ trong trường hợp bạn lười biếng như tôi, đây là một giải pháp được hợp nhất từ câu trả lời này và các câu trả lời khác về 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;
bounding_distance
đại diện? giá trị này có giới hạn kết quả trong một dặm không? Vì vậy, trong trường hợp này, nó sẽ trả về kết quả trong vòng 1 dặm?
Dễ dàng một;)
SELECT * FROM `WAYPOINTS` W ORDER BY
ABS(ABS(W.`LATITUDE`-53.63) +
ABS(W.`LONGITUDE`-9.9)) ASC LIMIT 30;
Chỉ cần thay thế tọa độ với những yêu cầu của bạn. Các giá trị phải được lưu trữ là gấp đôi. Đây là một ví dụ MySQL 5.x đang hoạt động.
Chúc mừng
dx+dy
Hãy thử điều này, nó hiển thị các điểm gần nhất với tọa độ được cung cấp (trong vòng 50 km). Nó hoạt động hoàn hảo:
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
Chỉ cần thay đổi <table_name>
. <userLat>
và<userLon>
Bạn có thể đọc thêm về giải pháp này tại đây: http://www.plumislandmedia.net/mysql/haversine-mysql-gầnest-loc/
Bạn đang tìm kiếm những thứ như công thức haversine . Xem tại đây là tốt.
Có những cái khác nhưng đây là trích dẫn phổ biến nhất.
Nếu bạn đang tìm kiếm thứ gì đó mạnh mẽ hơn nữa, bạn có thể muốn xem xét khả năng của cơ sở dữ liệu GIS. Chúng có khả năng làm một số điều thú vị như cho bạn biết liệu một điểm (Thành phố) có xuất hiện trong một đa giác nhất định (Vùng, Quốc gia, Lục địa) hay không.
Các câu trả lời ban đầu cho câu hỏi là tốt, nhưng các phiên bản mới hơn của mysql (MySQL 5.7.6 trở đi) hỗ trợ các truy vấn địa lý, do đó giờ đây bạn có thể sử dụng chức năng tích hợp thay vì thực hiện các truy vấn phức tạp.
Bây giờ bạn có thể làm một cái gì đó như:
select *, ST_Distance_Sphere( point ('input_longitude', 'input_latitude'),
point(longitude, latitude)) * .000621371192
as `distance_in_miles`
from `TableName`
having `distance_in_miles` <= 'input_max_distance'
order by `distance_in_miles` asc
Các kết quả được trả về trong meters
vì vậy nếu bạn muốn KM
thay vì dặm sử dụng .0001
thay vì.000621371192
ST_Distance_Sphere
không tồn tại trong cài đặt máy chủ của tôi ( mysql Ver 15.1 Distrib 10.2.23-MariaDB
). Tôi đọc ở đâu đó để thay thế ST_Distance
nhưng khoảng cách là cách.
ST_Distance_Sphere
Kiểm tra mã này dựa trên bài viết Geo-Khoảng cách-Tìm kiếm với MySQL :
Ví dụ: tìm ra 10 khách sạn gần vị trí hiện tại của tôi trong bán kính 10 dặm:
#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.
simpledb.execSQL("CREATE TABLE IF NOT EXISTS " + tablename + "(id INTEGER PRIMARY KEY AUTOINCREMENT,lat double,lng double,address varchar)");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2891001','70.780154','craftbox');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2901396','70.7782428','kotecha');");//22.2904718 //70.7783906
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2863155','70.772108','kkv Hall');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.275993','70.778076','nana mava');");
simpledb.execSQL("insert into '" + tablename + "'(lat,lng,address)values('22.2667148','70.7609386','Govani boys hostal');");
double curentlat=22.2667258; //22.2677258
double curentlong=70.76096826;//70.76096826
double curentlat1=curentlat+0.0010000;
double curentlat2=curentlat-0.0010000;
double curentlong1=curentlong+0.0010000;
double curentlong2=curentlong-0.0010000;
try{
Cursor c=simpledb.rawQuery("select * from '"+tablename+"' where (lat BETWEEN '"+curentlat2+"' and '"+curentlat1+"') or (lng BETWEEN '"+curentlong2+"' and '"+curentlong1+"')",null);
Log.d("SQL ", c.toString());
if(c.getCount()>0)
{
while (c.moveToNext())
{
double d=c.getDouble(1);
double d1=c.getDouble(2);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
Tìm người dùng gần nhất với tôi:
Khoảng cách tính bằng mét
Dựa trên công thức của Vincenty
tôi có bảng người dùng:
+----+-----------------------+---------+--------------+---------------+
| 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
bán kính trái đất: 6371000 (tính bằng mét)
Phiên bản MS SQL tại đây:
DECLARE @SLAT AS FLOAT
DECLARE @SLON AS FLOAT
SET @SLAT = 38.150785
SET @SLON = 27.360249
SELECT TOP 10 [LATITUDE], [LONGITUDE], SQRT(
POWER(69.1 * ([LATITUDE] - @SLAT), 2) +
POWER(69.1 * (@SLON - [LONGITUDE]) * COS([LATITUDE] / 57.3), 2)) AS distance
FROM [TABLE] ORDER BY 3
Âm thanh như bạn chỉ nên sử dụng PostGIS, SpatialLite, SQLServer2008 hoặc Oracle Spatial. Tất cả họ có thể trả lời câu hỏi này cho bạn bằng SQL không gian.
Trong trường hợp cực đoan, cách tiếp cận này không thành công, nhưng để thực hiện, tôi đã bỏ qua lượng giác và chỉ đơn giản là tính bình phương đường chéo.
Vấn đề này không khó lắm, nhưng nó sẽ phức tạp hơn nếu bạn cần tối ưu hóa nó.
Ý tôi là, bạn có 100 vị trí trong cơ sở dữ liệu của bạn hay 100 triệu? Điều đó gây ra một khác biệt lớn.
Nếu số lượng vị trí ít, hãy lấy chúng ra khỏi SQL và nhập mã bằng cách thực hiện ->
Select * from Location
Khi bạn nhận được chúng vào mã, hãy tính khoảng cách giữa mỗi lat / lon và bản gốc của bạn với công thức Haversine và sắp xếp nó.