Cách nhanh nhất để tìm khoảng cách giữa hai điểm Lat / Long


227

Tôi hiện chỉ có dưới một triệu vị trí trong cơ sở dữ liệu mysql với tất cả thông tin kinh độ và vĩ độ.

Tôi đang cố gắng tìm khoảng cách giữa một điểm và nhiều điểm khác thông qua truy vấn. Nó không nhanh như tôi muốn, đặc biệt là với hơn 100 lượt truy cập một giây.

Có một truy vấn nhanh hơn hoặc có thể là một hệ thống nhanh hơn ngoài mysql cho điều này? Tôi đang sử dụng truy vấn này:

SELECT 
  name, 
   ( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) ) 
   * cos( radians(locations.lng) - radians(-71.35368)) + sin(radians(42.290763)) 
   * sin( radians(locations.lat)))) AS distance 
FROM locations 
WHERE active = 1 
HAVING distance < 10 
ORDER BY distance;

Lưu ý: Khoảng cách được cung cấp là ở Miles . Nếu bạn cần Kilômét , sử dụng 6371thay vì 3959.


31
Công thức bạn đưa ra dường như có rất nhiều yếu tố không đổi. Có thể tính toán trước dữ liệu và lưu trữ các giá trị đó trong DB của bạn không? Ví dụ 3959 * acos (cos (radian (42.290763)) là một hằng số nhưng có 4 phép tính chính trong đó. Thay vào đó, bạn có thể lưu trữ 6696.7837 không?
Peter M

1
Hoặc ít nhất là các hằng số tính toán trước bên ngoài truy vấn? Điều đó sẽ cắt giảm công việc phải làm.
Peter M

2
@Peter M Có vẻ như bất kỳ cơ sở dữ liệu SQL phong nha nào cũng sẽ tối ưu hóa nên chỉ được tính một lần.
mhenry1384

25
Đối với những người thắc mắc, 42.290763 là vĩ độ và -71,35368 là kinh độ của điểm để tính khoảng cách.
dùng276648

14
Chỉ để biết, cách caluclated bởi công thức này là ở dặm, không phải trong kilometers.Please Thay 3959-6371 để có được kết quả bằng km
Sahil

Câu trả lời:


115
  • Tạo điểm của bạn bằng cách sử dụng Pointcác giá trị của các Geometryloại dữ liệu trong MyISAMbảng. Kể từ Mysql 5.7.5, InnoDBcác bảng hiện cũng hỗ trợ SPATIALcác chỉ mục.

  • Tạo một SPATIALchỉ mục về những điểm này

  • Sử dụng MBRContains()để tìm các giá trị:

    SELECT  *
    FROM    table
    WHERE   MBRContains(LineFromText(CONCAT(
            '('
            , @lon + 10 / ( 111.1 / cos(RADIANS(@lon)))
            , ' '
            , @lat + 10 / 111.1
            , ','
            , @lon - 10 / ( 111.1 / cos(RADIANS(@lat)))
            , ' '
            , @lat - 10 / 111.1 
            , ')' )
            ,mypoint)

hoặc, trong MySQL 5.1và trên:

    SELECT  *
    FROM    table
    WHERE   MBRContains
                    (
                    LineString
                            (
                            Point (
                                    @lon + 10 / ( 111.1 / COS(RADIANS(@lat))),
                                    @lat + 10 / 111.1
                                  ),
                            Point (
                                    @lon - 10 / ( 111.1 / COS(RADIANS(@lat))),
                                    @lat - 10 / 111.1
                                  ) 
                            ),
                    mypoint
                    )

Điều này sẽ chọn tất cả các điểm xấp xỉ trong hộp (@lat +/- 10 km, @lon +/- 10km).

Đây thực sự không phải là một hộp, mà là một hình chữ nhật hình cầu: đoạn giới hạn vĩ độ và kinh độ của hình cầu. Điều này có thể khác với một hình chữ nhật đơn giản trên Franz Joseph Land , nhưng khá gần với nó trên hầu hết các nơi có người ở.

  • Áp dụng lọc bổ sung để chọn mọi thứ bên trong vòng tròn (không phải hình vuông)

  • Có thể áp dụng bộ lọc mịn bổ sung để tính khoảng cách vòng tròn lớn (đối với khoảng cách lớn)


15
@Quassnoi: Một vài chỉnh sửa: Có thể bạn sẽ muốn chuyển thứ tự tọa độ sang lat, lâu. Ngoài ra, khoảng cách dọc là tỷ lệ cosin của vĩ độ , không phải kinh độ. Và bạn sẽ muốn thay đổi nó từ phép nhân sang phép chia, do đó tọa độ đầu tiên của bạn sẽ được sửa thành @lon - 10 / ( 111.1 / cos(@lat))(và là số thứ hai trong cặp sau khi mọi thứ đều chính xác.
M. Dave Auayan

8
CẢNH BÁO : Phần thân của câu trả lời KHÔNG được chỉnh sửa để phù hợp với nhận xét rất hợp lệ được thực hiện bởi @M. Dave Auaya. Ghi chú thêm: Phương pháp này được thực hiện nếu vòng tròn quan tâm (a) bao gồm một cực hoặc (b) được giao nhau bởi kinh tuyến +/- 180 độ kinh độ. Cũng sử dụng cos(lon)là chính xác chỉ cho khoảng cách nhỏ. Xem janmatuschek.de/LatitudeLongitudeBoundingCoord tọa độ
John Machin

3
Có cách nào để chúng ta có thể hiểu rõ hơn về những gì các hằng số (10, 111.11, @lat, @lon, mypoint) đại diện không? Tôi giả sử rằng 10 là cho khoảng cách km, @lat và @lon đại diện cho mạng và kinh độ được cung cấp, nhưng 111.11 và mypoint thể hiện điều gì trong ví dụ?
ashays

4
@ashays: có khoảng 111.(1)km trong một độ vĩ độ. mypointlà trường trong bảng lưu trữ tọa độ.
Quassnoi

1
Một sửa lỗi khác - bạn đang thiếu một lần đóng) ở dòng thứ hai đến dòng cuối cùng
ina

100

Không phải là câu trả lời cụ thể của MySql, nhưng nó sẽ cải thiện hiệu suất của câu lệnh sql của bạn.

Những gì bạn đang làm hiệu quả là tính toán khoảng cách đến mọi điểm trong bảng, để xem liệu nó có nằm trong 10 đơn vị của một điểm nhất định không.

Những gì bạn có thể làm trước khi bạn chạy sql này, là tạo bốn điểm vẽ một hộp 20 đơn vị ở một bên, với điểm của bạn ở trung tâm tức là. (x1, y1). . . (x4, y4), trong đó (x1, y1) là (cho dài + 10 đơn vị, đã choLat + 10 đơn vị). . . (đã choLong - 10 đơn vị, đã cho đơn vị -10 đơn vị). Trên thực tế, bạn chỉ cần hai điểm, trên cùng bên trái và dưới cùng bên phải gọi chúng (X1, Y1) và (X2, Y2)

Bây giờ câu lệnh SQL của bạn sử dụng các điểm này để loại trừ các hàng chắc chắn lớn hơn 10u so với điểm đã cho của bạn, nó có thể sử dụng các chỉ mục về vĩ độ và kinh độ, do đó sẽ là các đơn đặt hàng có cường độ nhanh hơn so với những gì bạn hiện có.

ví dụ

select . . . 
where locations.lat between X1 and X2 
and   locations.Long between y1 and y2;

Cách tiếp cận hộp có thể trả về giá trị dương (bạn có thể lấy điểm ở các góc của hộp> 10u từ điểm đã cho), vì vậy bạn vẫn cần tính khoảng cách của mỗi điểm. Tuy nhiên, điều này một lần nữa sẽ nhanh hơn nhiều vì bạn đã hạn chế đáng kể số điểm cần kiểm tra vào các điểm trong hộp.

Tôi gọi kỹ thuật này là "Suy nghĩ bên trong chiếc hộp" :)

EDIT: Điều này có thể được đưa vào một câu lệnh SQL không?

Tôi không biết mySql hoặc Php có khả năng gì, xin lỗi. Tôi không biết nơi tốt nhất để xây dựng bốn điểm hoặc làm thế nào chúng có thể được chuyển đến truy vấn mySql trong Php. Tuy nhiên, khi bạn có bốn điểm, không có gì ngăn cản bạn kết hợp câu lệnh SQL của riêng bạn với câu lệnh của tôi.

select name, 
       ( 3959 * acos( cos( radians(42.290763) ) 
              * cos( radians( locations.lat ) ) 
              * cos( radians( locations.lng ) - radians(-71.35368) ) 
              + sin( radians(42.290763) ) 
              * sin( radians( locations.lat ) ) ) ) AS distance 
from locations 
where active = 1 
and locations.lat between X1 and X2 
and locations.Long between y1 and y2
having distance < 10 ORDER BY distance;

Tôi biết với MS SQL Tôi có thể xây dựng một câu lệnh SQL khai báo bốn số float (X1, Y1, X2, Y2) và tính toán chúng trước câu lệnh chọn "chính", như tôi đã nói, tôi không biết liệu điều này có thể được thực hiện với MySql. Tuy nhiên, tôi vẫn có xu hướng xây dựng bốn điểm trong C # và chuyển chúng dưới dạng tham số cho truy vấn SQL.

Xin lỗi tôi không thể giúp đỡ nhiều hơn, nếu bất cứ ai có thể trả lời các phần cụ thể của MySQL & Php về điều này, vui lòng chỉnh sửa câu trả lời này để làm như vậy.


4
Bạn có thể tìm thấy một quy trình mysql cho cách tiếp cận này trong bản trình bày này: scribd.com/doc/2569355/Geo-Distance-Search-with-MyQuery
Lucia

37
Để tìm kiếm theo km thay vì dặm, thay thế 3959 với 6371.
ErichBSchulz

4
+1, tùy chọn tuyệt vời; thêm hộp giảm truy vấn của tôi từ 4 giây xuống còn 0,03 giây.
jvenema

1
Rõ ràng là rất logic, bạn dành một giải thưởng cho giải pháp này! Trên cơ sở dữ liệu bản ghi 2 triệu, truy vấn đã chuyển từ 16 giây xuống 0,06 giây. Lưu ý: Nó thậm chí còn nhanh hơn (đối với các bảng lớn) nếu bạn cắt phép tính khoảng cách ra khỏi truy vấn và thực hiện phép tính cho khoảng cách trong mã chương trình của bạn!
NLAnaconda

2
@Binary Worrier: Vì vậy, X1, X2 và Y1, Y2 sẽ là Longitude Min và Max và Latitude Min và Max theo ví dụ được đưa ra ở đây: blog.fedecarg.com/2009/02/08/ xin vui lòng tư vấn.
Prabhat

14

Các chức năng MySQL sau đây đã được đăng trên bài đăng blog này . Tôi đã không kiểm tra nó nhiều, nhưng từ những gì tôi thu thập được từ bài đăng, nếu các trường vĩ độ và kinh độ của bạn được lập chỉ mục , điều này có thể hoạt động tốt cho bạn:

DELIMITER $$

DROP FUNCTION IF EXISTS `get_distance_in_miles_between_geo_locations` $$
CREATE FUNCTION get_distance_in_miles_between_geo_locations(
  geo1_latitude decimal(10,6), geo1_longitude decimal(10,6), 
  geo2_latitude decimal(10,6), geo2_longitude decimal(10,6)) 
returns decimal(10,3) DETERMINISTIC
BEGIN
  return ((ACOS(SIN(geo1_latitude * PI() / 180) * SIN(geo2_latitude * PI() / 180) 
    + COS(geo1_latitude * PI() / 180) * COS(geo2_latitude * PI() / 180) 
    * COS((geo1_longitude - geo2_longitude) * PI() / 180)) * 180 / PI()) 
    * 60 * 1.1515);
END $$

DELIMITER ;

Sử dụng mẫu:

Giả sử một bảng được gọi placesvới các trường latitude& longitude:

SELECT get_distance_in_miles_between_geo_locations(-34.017330, 22.809500,
latitude, longitude) AS distance_from_input FROM places;

Tôi đã thử điều này và nó hoạt động hoàn hảo, nhưng bằng cách nào đó, nó không cho phép tôi đưa vào câu lệnh WHERE dựa trên distance_from_input. Bất cứ ý tưởng tại sao không?
Chris Visser

bạn có thể thực hiện dưới dạng phụ chọn: chọn * từ (...) là t trong đó distance_from_input> 5;
Brad park

2
hoặc chỉ cần đi thẳng với: select * từ những nơi có get_distance_in_miles_b between_geo_locations (-34,017330, 22,809500, vĩ độ, kinh độ)> 5000;
Brad park

2
mét trở về:SELECT ROUND(((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((lnt1 - lnt2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) * 1.609344 * 1000) AS distance
Mohammad

13

Tôi cần giải quyết vấn đề tương tự (lọc các hàng theo khoảng cách từ một điểm) và bằng cách kết hợp câu hỏi ban đầu với câu trả lời và nhận xét, tôi đã đưa ra giải pháp hoàn toàn phù hợp với tôi trên cả MySQL 5.6 và 5.7.

SELECT 
    *,
    (6371 * ACOS(COS(RADIANS(56.946285)) * COS(RADIANS(Y(coordinates))) 
    * COS(RADIANS(X(coordinates)) - RADIANS(24.105078)) + SIN(RADIANS(56.946285))
    * SIN(RADIANS(Y(coordinates))))) AS distance
FROM places
WHERE MBRContains
    (
    LineString
        (
        Point (
            24.105078 + 15 / (111.320 * COS(RADIANS(56.946285))),
            56.946285 + 15 / 111.133
        ),
        Point (
            24.105078 - 15 / (111.320 * COS(RADIANS(56.946285))),
            56.946285 - 15 / 111.133
        )
    ),
    coordinates
    )
HAVING distance < 15
ORDER By distance

coordinateslà trường có loại POINTvà có SPATIALchỉ số
6371để tính khoảng cách theo km
56.946285là vĩ độ cho điểm trung tâm
24.105078là kinh độ cho điểm trung tâm
15là khoảng cách tối đa tính bằng km

Trong các thử nghiệm của tôi, MySQL sử dụng chỉ mục SPATIAL trên coordinatestrường để nhanh chóng chọn tất cả các hàng trong hình chữ nhật và sau đó tính khoảng cách thực tế cho tất cả các vị trí được lọc để loại trừ các vị trí khỏi các góc hình chữ nhật và chỉ để lại các vị trí trong vòng tròn.

Đây là hình dung về kết quả của tôi:

bản đồ

Các ngôi sao màu xám trực quan hóa tất cả các điểm trên bản đồ, các ngôi sao màu vàng là những điểm được trả về bởi truy vấn MySQL. Các ngôi sao màu xám bên trong các góc của hình chữ nhật (nhưng hình tròn bên ngoài) đã được chọn MBRContains()và sau đó bỏ chọn theo HAVINGmệnh đề.


Không thể nâng cao điều này đủ. Tìm kiếm thông qua một bảng với khoảng 5 triệu bản ghi và chỉ mục không gian với phương pháp này, thời gian tìm kiếm là 0,005 giây trên bộ xử lý A8 cũ. Tôi biết rằng 6371 có thể được thay thế bằng 3959 để có được kết quả trong dặm nhưng làm các giá trị của 111,133 và 111,320 cần phải được điều chỉnh hoặc là họ phổ liên tục?
Wranorn

Giải pháp tuyệt vời.
SeaBcakes

Cách tạo Point là POINT (lat, lng) hoặc POINT (lng, lat)
user606669

2
@ user606669 Đó là ĐIỂM (lng, lat)
Māris Kiseļovs

Hàm X () và Y () nên là ST_Y và ST_X ngày nay.
Andreas

11

nếu bạn đang sử dụng MySQL 5.7. *, thì bạn có thể sử dụng st_distance_sphere (POINT, POINT) .

Select st_distance_sphere(POINT(-2.997065, 53.404146 ), POINT(58.615349, 23.56676 ))/1000  as distcance

1
Đây là một thay thế rất tốt và dễ đọc. Xin lưu ý, thứ tự tham số cho POINT () là (lng, lat) nếu không bạn có thể sẽ kết thúc bằng "đóng" nhưng vẫn cho kết quả rất khác so với các phương thức khác ở đây. xem: stackoverflow.com/questions/35939853/ khăn
Andy P

9
SELECT * FROM (SELECT *,(((acos(sin((43.6980168*pi()/180)) * 
sin((latitude*pi()/180))+cos((43.6980168*pi()/180)) * 
cos((latitude*pi()/180)) * cos(((7.266903899999988- longitude)* 
pi()/180))))*180/pi())*60*1.1515 ) as distance 
FROM wp_users WHERE 1 GROUP BY ID limit 0,10) as X 
ORDER BY ID DESC

Đây là truy vấn tính toán khoảng cách giữa các điểm trong MySQL, tôi đã sử dụng nó trong một cơ sở dữ liệu dài, nó hoạt động hoàn hảo! Lưu ý: thực hiện các thay đổi (tên cơ sở dữ liệu, tên bảng, cột, v.v.) theo yêu cầu của bạn.


Giá trị 1.1515 thể hiện điều gì? Tôi đã thấy một công thức tương tự trước đây, nhưng nó đã sử dụng 1,75 thay vì 1,1515.
TryHarder

1
Trả lời câu hỏi của riêng tôi, tôi nghĩ rằng câu trả lời có thể nằm ở đây stackoverflow.com/a/389251/691053
TryHarder

8
set @latitude=53.754842;
set @longitude=-2.708077;
set @radius=20;

set @lng_min = @longitude - @radius/abs(cos(radians(@latitude))*69);
set @lng_max = @longitude + @radius/abs(cos(radians(@latitude))*69);
set @lat_min = @latitude - (@radius/69);
set @lat_max = @latitude + (@radius/69);

SELECT * FROM postcode
WHERE (longitude BETWEEN @lng_min AND @lng_max)
AND (latitude BETWEEN @lat_min and @lat_max);

nguồn


11
Vui lòng trích dẫn nguồn của bạn. Đây là từ: blog.fedecarg.com/2009/02/08/ Mạnh
redburn

69 trong trường hợp này là gì? Làm thế nào để làm trong trường hợp nếu chúng ta có bán kính trái đất?
CodeRunner

2
Kilômét trong 1 Latittude là 111 KM. Mile trong 1 Latittude là 69 dặm. và 69 Miles = 111 KM. Đó là lý do tại sao chúng tôi đã sử dụng các tham số trong chuyển đổi.
CodeRunner

Tôi đã tìm kiếm điều này mãi mãi. Không biết nó có thể đơn giản như vậy. Cảm ơn bạn rất nhiều.
Vikas

Điều này có sai không vì lng_min / lng_max sẽ cần sử dụng lat_min và lat_max trong toán học bán kính?
Ben

6
   select
   (((acos(sin(('$latitude'*pi()/180)) * sin((`lat`*pi()/180))+cos(('$latitude'*pi()/180)) 
    * cos((`lat`*pi()/180)) * cos((('$longitude'- `lng`)*pi()/180))))*180/pi())*60*1.1515) 
    AS distance
    from table having distance<22;

5

Hàm MySQL trả về số mét giữa hai tọa độ:

CREATE FUNCTION DISTANCE_BETWEEN (lat1 DOUBLE, lon1 DOUBLE, lat2 DOUBLE, lon2 DOUBLE)
RETURNS DOUBLE DETERMINISTIC
RETURN ACOS( SIN(lat1*PI()/180)*SIN(lat2*PI()/180) + COS(lat1*PI()/180)*COS(lat2*PI()/180)*COS(lon2*PI()/180-lon1*PI()/180) ) * 6371000

Để trả về giá trị ở định dạng khác, hãy thay thế 6371000hàm trong bán kính Trái đất theo đơn vị bạn chọn. Ví dụ, mới lăn bánh sẽ 6371và dặm sẽ 3959.

Để sử dụng chức năng, chỉ cần gọi nó như bất kỳ chức năng nào khác trong MySQL. Ví dụ: nếu bạn có một bảng city, bạn có thể tìm thấy khoảng cách giữa mọi thành phố với mọi thành phố khác:

SELECT
    `city1`.`name`,
    `city2`.`name`,
    ROUND(DISTANCE_BETWEEN(`city1`.`latitude`, `city1`.`longitude`, `city2`.`latitude`, `city2`.`longitude`)) AS `distance`
FROM
    `city` AS `city1`
JOIN
    `city` AS `city2`

4

Mã đầy đủ với các chi tiết về cách cài đặt như plugin MySQL có tại đây: https://github.com/lucasepe/lib_mysqludf_haversine

Tôi đã đăng bài này vào năm ngoái dưới dạng bình luận. Vì vui lòng @TylerCollier đề nghị tôi đăng bài dưới dạng câu trả lời, đây rồi.

Một cách khác là viết một hàm UDF tùy chỉnh trả về khoảng cách haversine từ hai điểm. Chức năng này có thể mất trong đầu vào:

lat1 (real), lng1 (real), lat2 (real), lng2 (real), type (string - optinal - 'km', 'ft', 'mi')

Vì vậy, chúng ta có thể viết một cái gì đó như thế này:

SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2) < 40;

để lấy tất cả các bản ghi với khoảng cách ít hơn 40 km. Hoặc là:

SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2, 'ft') < 25;

để lấy tất cả các bản ghi với khoảng cách ít hơn 25 feet.

Chức năng cốt lõi là:

double
haversine_distance( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ) {
    double result = *(double*) initid->ptr;
    /*Earth Radius in Kilometers.*/ 
    double R = 6372.797560856;
    double DEG_TO_RAD = M_PI/180.0;
    double RAD_TO_DEG = 180.0/M_PI;
    double lat1 = *(double*) args->args[0];
    double lon1 = *(double*) args->args[1];
    double lat2 = *(double*) args->args[2];
    double lon2 = *(double*) args->args[3];
    double dlon = (lon2 - lon1) * DEG_TO_RAD;
    double dlat = (lat2 - lat1) * DEG_TO_RAD;
    double a = pow(sin(dlat * 0.5),2) + 
        cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD) * pow(sin(dlon * 0.5),2);
    double c = 2.0 * atan2(sqrt(a), sqrt(1-a));
    result = ( R * c );
    /*
     * If we have a 5th distance type argument...
     */
    if (args->arg_count == 5) {
        str_to_lowercase(args->args[4]);
        if (strcmp(args->args[4], "ft") == 0) result *= 3280.8399;
        if (strcmp(args->args[4], "mi") == 0) result *= 0.621371192;
    }

    return result;
}

3

Một phép tính gần đúng nhanh, đơn giản và chính xác (cho khoảng cách nhỏ hơn) có thể được thực hiện với phép chiếu hình cầu . Ít nhất trong thuật toán định tuyến của tôi, tôi được tăng 20% ​​so với tính toán chính xác. Trong mã Java, nó trông giống như:

public double approxDistKm(double fromLat, double fromLon, double toLat, double toLon) {
    double dLat = Math.toRadians(toLat - fromLat);
    double dLon = Math.toRadians(toLon - fromLon);
    double tmp = Math.cos(Math.toRadians((fromLat + toLat) / 2)) * dLon;
    double d = dLat * dLat + tmp * tmp;
    return R * Math.sqrt(d);
}

Không chắc chắn về MySQL (xin lỗi!).

Hãy chắc chắn rằng bạn biết về giới hạn (thông số thứ ba của assertEquals có nghĩa là độ chính xác tính bằng km):

    float lat = 24.235f;
    float lon = 47.234f;
    CalcDistance dist = new CalcDistance();
    double res = 15.051;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);

    res = 150.748;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 1, lon + 1), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 1, lon + 1), 1e-2);

    res = 1527.919;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 10, lon + 10), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 10, lon + 10), 10);

3

Dưới đây là một mô tả rất chi tiết về Tìm kiếm khoảng cách địa lý với MySQL một giải pháp dựa trên việc triển khai Công thức Haversine cho mysql. Mô tả giải pháp hoàn chỉnh với lý thuyết, thực hiện và tối ưu hóa hiệu suất hơn nữa. Mặc dù phần tối ưu hóa không gian không hoạt động chính xác trong trường hợp của tôi. http://www.scribed.com/doc/2569355/Geo-Distance-Search-with-MyQuery


3

Hãy đọc Tìm kiếm khoảng cách địa lý với MySQL , một giải pháp dựa trên việc triển khai Công thức Haversine cho MySQL. Đây là một mô tả giải pháp hoàn chỉnh với lý thuyết, thực hiện và tối ưu hóa hiệu suất hơn nữa. Mặc dù phần tối ưu hóa không gian không hoạt động chính xác trong trường hợp của tôi.

Tôi nhận thấy hai sai lầm trong việc này:

  1. việc sử dụng abstrong câu lệnh chọn trên p8. Tôi chỉ bỏ qua absvà nó đã làm việc.

  2. hàm khoảng cách tìm kiếm không gian trên p27 không chuyển đổi thành radian hoặc nhân kinh độ cos(latitude), trừ khi dữ liệu không gian của anh ta được tải với điều này (không thể nói từ ngữ cảnh của bài viết), nhưng ví dụ của anh ta trên p26 chỉ ra rằng dữ liệu không gian của anh ta không POINTđược tải radian hoặc độ.


0
$objectQuery = "SELECT table_master.*, ((acos(sin((" . $latitude . "*pi()/180)) * sin((`latitude`*pi()/180))+cos((" . $latitude . "*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((" . $longitude . "- `longtude`)* pi()/180))))*180/pi())*60*1.1515  as distance FROM `table_post_broadcasts` JOIN table_master ON table_post_broadcasts.master_id = table_master.id WHERE table_master.type_of_post ='type' HAVING distance <='" . $Radius . "' ORDER BY distance asc";

0

Sử dụng mysql

SET @orig_lon = 1.027125;
SET @dest_lon = 1.027125;

SET @orig_lat = 2.398441;
SET @dest_lat = 2.398441;

SET @kmormiles = 6371;-- for distance in miles set to : 3956

SELECT @kmormiles * ACOS(LEAST(COS(RADIANS(@orig_lat)) * 
 COS(RADIANS(@dest_lat)) * COS(RADIANS(@orig_lon - @dest_lon)) + 
 SIN(RADIANS(@orig_lat)) * SIN(RADIANS(@dest_lat)),1.0)) as distance;

Xem: https://andrew.hedges.name/experiment/haversine/

Xem: https://stackoverflow.com/a/24372831/5155484

Xem: http://www.plumislandmedia.net/mysql/haversine-mysql-gầnest-loc/

LƯU Ý: LEASTđược sử dụng để tránh các giá trị null dưới dạng nhận xét được đề xuất trên https://stackoverflow.com/a/24372831/5155484

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.