Sử dụng geohash cho các tìm kiếm gần?


30

Tôi đang tìm cách tối ưu hóa thời gian tìm kiếm địa lý gần điểm.

Đầu vào của tôi là lat, điểm lng và tôi đang tìm kiếm trên một tập hợp các vị trí được tính toán trước đến n điểm gần nhất.

Tôi không quan tâm việc xây dựng chỉ mục các vị trí được tính toán trước sẽ mất bao nhiêu thời gian nhưng tôi quan tâm các truy vấn sẽ siêu nhanh.

Tôi đang suy nghĩ về việc sử dụng geohash làm khóa tìm kiếm, nơi đầu tiên tôi sẽ kiểm tra xem tôi có nhận được kết quả cho các ký tự X của khóa hay không và sau đó tiếp tục cắt bớt các ký tự từ cuối khóa cho đến khi tôi bắt đầu thấy kết quả.

Theo hiểu biết của tôi (rất thưa thớt bây giờ) về các kỹ thuật chỉ mục địa lý, phương pháp này có thể tạo ra kết quả nhanh nhất (về thời gian truy vấn) so với tất cả các triển khai đã biết khác (như R Tree và co.)


Có sự khác biệt đáng kể nào giữa việc sử dụng geohash và lưu trữ lat / long của bạn ở hướng đông / hướng bắc (chẳng hạn) không? Có lẽ với cả hai bạn có thể thay đổi độ chính xác tìm kiếm của mình bằng cách cắt bớt ký tự / chữ số. (Đây hoàn toàn là một câu hỏi vì tò mò - Tôi không quen thuộc với chủ đề này).
djq

Những điểm này được lưu trữ trong cơ sở dữ liệu hoặc trong bộ nhớ hay?
Marc Pfister

@MarcPfister vấn đề này đã được 2 năm tuổi (đối với trường hợp sử dụng của tôi) nhưng nó luôn phù hợp với cộng đồng nên tôi sẽ tiếp tục thảo luận tích cực. Dữ liệu thảo luận đã thực sự được lưu trữ trong cơ sở dữ liệu nosql.
Maxim Veksler

Ngoài ra, tôi tin rằng từ khi câu hỏi này được trả lời, MongoDB đã thực hiện thành công việc lập chỉ mục và tìm kiếm geohash, điều này chứng tỏ điểm này. Tôi chưa thấy một tờ giấy trắng về việc triển khai nhưng mã này được mở và có sẵn cho bất kỳ bên nào quan tâm.
Maxim Veksler

À, được rồi CouchDB cũng đã lập chỉ mục không gian, có lẽ cũng sử dụng geohash.
Marc Pfister

Câu trả lời:


25

Hoàn toàn có thể. Và nó có thể khá nhanh. (Các bit tính toán chuyên sâu C ALNG có thể được phân phối)

Có một số cách, nhưng một cách mà tôi đã làm việc là sử dụng danh sách các geohash dựa trên số nguyên và tìm tất cả các phạm vi geohash lân cận gần nhất cho độ phân giải geohash cụ thể (độ phân giải xấp xỉ distancetiêu chí của bạn ), sau đó truy vấn các phạm vi geohash để có danh sách các điểm gần đó. Tôi sử dụng redis và nodejs (tức là javascript) cho việc này. Redis cực nhanh và có thể truy xuất các phạm vi được đặt hàng rất nhanh, nhưng nó không thể thực hiện được nhiều công cụ thao tác truy vấn lập chỉ mục mà cơ sở dữ liệu SQL có thể làm.

Phương pháp được phác thảo tại đây: https://github.com/yinqiwen/ardb/wiki/Spatial- Index

Nhưng ý chính của nó là (để diễn giải liên kết):

  1. Bạn lưu trữ tất cả các điểm địa lý của bạn ở độ phân giải tốt nhất bạn muốn (thường là số nguyên tối đa 64 bit nếu có thể truy cập được hoặc trong trường hợp javascript, 52 bit) trong một bộ được đặt hàng (ví dụ: zset in redis). Hầu hết các thư viện geohash ngày nay đều có các hàm số nguyên geohash được tích hợp và bạn sẽ cần sử dụng các hàm này thay vì các geohash cơ sở phổ biến hơn.
  2. Dựa trên bán kính bạn muốn tìm kiếm bên trong, bạn cần tìm độ sâu / độ phân giải bit phù hợp với khu vực tìm kiếm của bạn và giá trị này phải nhỏ hơn hoặc bằng độ sâu bit geohash được lưu trữ của bạn. Trang web được liên kết có một bảng tương quan với độ sâu bit của geohash với diện tích hộp giới hạn tính bằng mét.
  3. Sau đó, bạn thử lại tọa độ ban đầu của bạn ở độ phân giải thấp hơn này.
  4. Ở độ phân giải thấp hơn cũng tìm thấy 8 vùng lân cận (n, ne, e, se, s, sw, w, nw). Lý do tại sao bạn phải thực hiện phương pháp lân cận là bởi vì hai tọa độ gần đúng bên cạnh nhau có thể có các địa lý hoàn toàn khác nhau, do đó bạn cần thực hiện một số trung bình của khu vực được bao phủ bởi tìm kiếm.
  5. Khi bạn nhận được tất cả các geohash lân cận ở độ phân giải thấp hơn này, hãy thêm vào danh sách geohash tọa độ của bạn từ bước 3.
  6. Sau đó, bạn cần xây dựng một loạt các giá trị geohash để tìm kiếm trong đó bao gồm 9 khu vực này. Các giá trị từ bước 5 là giới hạn phạm vi thấp hơn của bạn và nếu bạn thêm 1 cho mỗi trong số chúng, bạn sẽ nhận được giới hạn phạm vi trên. Vì vậy, bạn nên có một mảng gồm 9 phạm vi, mỗi phạm vi có giới hạn dưới và giới hạn geohash trên (tổng cộng 18 geohash). Các geohash này vẫn ở độ phân giải thấp hơn từ bước 2.
  7. Sau đó, bạn chuyển đổi tất cả 18 trong số các geohash này thành bất kỳ độ sâu / độ phân giải bit nào mà bạn đã lưu trữ tất cả các geohash trong cơ sở dữ liệu của mình. Nói chung, bạn làm điều này bằng cách chuyển bit đến độ sâu bit mong muốn.
  8. Bây giờ bạn có thể thực hiện truy vấn phạm vi cho các điểm trong 9 phạm vi này và bạn sẽ nhận được tất cả các điểm trong khoảng cách từ điểm ban đầu của mình. Sẽ không có sự chồng chéo, do đó bạn không cần thực hiện bất kỳ giao lộ nào, chỉ là các truy vấn phạm vi thuần túy, rất nhanh. (ví dụ: trong redis: ZRANGEBYSCORE zsetname lowLimit UpperLimit, trong 9 phạm vi được tạo trong bước này)

Bạn có thể tối ưu hóa hơn nữa (tốc độ khôn ngoan) bằng cách:

  1. Lấy 9 phạm vi đó từ bước 6 và tìm nơi chúng dẫn vào nhau. Thông thường, bạn có thể giảm 9 phạm vi riêng biệt thành khoảng 4 hoặc 5 tùy thuộc vào vị trí tọa độ của bạn. Điều này có thể giảm một nửa thời gian truy vấn của bạn.
  2. Khi bạn có phạm vi cuối cùng của mình, bạn nên giữ chúng để sử dụng lại. Việc tính toán các phạm vi này có thể mất phần lớn thời gian xử lý, vì vậy nếu tọa độ ban đầu của bạn không thay đổi nhiều nhưng bạn cần thực hiện lại cùng một truy vấn khoảng cách, bạn nên giữ sẵn sàng thay vì tính toán mọi lúc.
  3. Nếu bạn đang sử dụng redis, hãy thử kết hợp các truy vấn thành MULTI / EXEC để nó xử lý chúng để có hiệu suất tốt hơn một chút.
  4. Phần TỐT NHẤT: Bạn có thể phân phối các bước 2-7 cho khách hàng thay vì thực hiện tính toán đó ở một nơi. Điều này giúp giảm đáng kể tải CPU trong các tình huống có hàng triệu yêu cầu được gửi đến.

Bạn có thể cải thiện độ chính xác hơn nữa bằng cách sử dụng hàm loại khoảng cách vòng / haversine trên kết quả trả về nếu bạn quan tâm nhiều đến độ chính xác.

Đây là một kỹ thuật tương tự bằng cách sử dụng geohash cơ sở thông thường và truy vấn SQL thay vì redis: https://github.com/davetroy/geohash-js

Tôi không có ý định cắm thứ của riêng tôi, nhưng tôi đã viết một mô-đun cho nodejs & redis giúp việc này thực sự dễ thực hiện. Hãy xem mã nếu bạn thích: https://github.com/arjunmehta/node-georedis


Một vài theo dõi Q - Làm thế nào để bạn tính toán hàng xóm? Là số nguyên băm cho phép cắt tỉa (base32 z-đường cong dựa không, cho ex. (7 là rất xa từ 8 trong base32 geohash). Làm thế nào là phương pháp được nêu trong geohash-js github.com/davetroy/geohash-js/blob/ master / matrix.txt tương tự trong khi thuật toán này phải tạo ra sự gần gũi địa điểm geohash-js làm O (1) tính toán của các tế bào hàng xóm chỉ?.
Maxim Veksler

Wow, điều này rất hữu ích. Rất nhiều chuyên môn trong phản ứng này. Nhiệm vụ khá khó khăn
simon

9

Câu hỏi có thể được đọc theo nhiều cách. Tôi hiểu điều đó có nghĩa là bạn có một số lượng lớn các điểm và bạn dự định thăm dò chúng liên tục với các điểm tùy ý, được đưa ra dưới dạng các cặp tọa độ và muốn lấy n điểm gần nhất với đầu dò, với n được cố định trước. (Về nguyên tắc, nếu n sẽ thay đổi, bạn có thể thiết lập cấu trúc dữ liệu cho mọi n có thể và chọn nó trong thời gian O (1) với mỗi đầu dò: việc này có thể mất nhiều thời gian thiết lập và cần nhiều RAM, nhưng chúng tôi được nói để bỏ qua những mối quan tâm như vậy.)

Xây dựng sơ đồ Voronoi theo thứ tự của tất cả các điểm. Điều này phân vùng máy bay thành các vùng được kết nối, mỗi vùng có cùng n hàng xóm. Điều này làm giảm tình trạng vấn đề đa giác, có nhiều giải pháp hiệu quả.

Sử dụng cấu trúc dữ liệu vectơ cho sơ đồ Voronoi, các tìm kiếm điểm đa giác sẽ mất thời gian O (log (n)). Đối với các mục đích thực tế, bạn có thể thực hiện O (1) này với hệ số ẩn cực kỳ nhỏ chỉ bằng cách tạo một phiên bản raster của sơ đồ. Các giá trị của các ô trong raster là (i) một con trỏ tới danh sách n điểm gần nhất hoặc (ii) một dấu hiệu cho thấy ô này nằm trên hai hoặc nhiều vùng trong sơ đồ. Bài kiểm tra cho một điểm tùy ý tại (x, y) trở thành:

Fetch the cell value for (x,y).
If the value is a list of points, return it.
Else apply a vector point-in-polygon algorithm to (x,y).

Để đạt được hiệu suất O (1), lưới raster phải đủ tốt để tương đối ít điểm thăm dò sẽ rơi vào các ô nằm giữa nhiều vùng Voronoi. Điều này luôn có thể được thực hiện, với một chi phí rất lớn trong việc lưu trữ cho các lưới.


3

Tôi sử dụng geohash cho chính xác điều này. Lý do tôi là bởi vì tôi cần phải thực hiện các tìm kiếm gần bằng cách sử dụng hệ thống thông tin kiểu kim tự tháp .. trong đó các geohash với độ chính xác cấp 8 là 'cơ sở' và tạo thành các tổng số mới cho geohash của độ chính xác thứ 7, v.v. . Các tổng số này là diện tích, các loại che phủ mặt đất, vv .. Đó là một cách rất lạ mắt để làm một số thứ rất lạ mắt.

Vì vậy, geohash cấp 8 sẽ chứa thông tin như:

loại: mẫu đất trồng cỏ: 1,23

và thứ 7, thứ 6 .. vv .. sẽ chứa thông tin như:

cỏ_types: 123 mẫu: 6502

Điều này luôn được xây dựng từ độ chính xác thấp nhất. Điều này cho phép tôi thực hiện tất cả các loại thống kê thú vị rất nhanh. Tôi cũng có thể chỉ định một tham chiếu hình học cho mỗi tham chiếu geohash bằng GeoJSON.

Tôi đã có thể viết một số hàm để tìm các geohash lớn nhất tạo nên khung nhìn hiện tại của tôi và sau đó sử dụng các hàm đó để tìm các geohash có độ chính xác lớn thứ hai trong khung nhìn. Điều này có thể dễ dàng được mở rộng cho các truy vấn phạm vi được lập chỉ mục trong đó tôi sẽ truy vấn tối thiểu là '86ssaaaa' và tối đa là '86sszzzz' cho bất kỳ độ chính xác nào tôi muốn.

Tôi đang làm điều này bằng cách sử dụng MongoDB.


3

Cập nhật cho năm 2018 và một số quỹ toán học hoặc nguồn gốc lịch sử của Geohash:

  • nguồn cảm hứng cho Geohash là interlave đơn giản của các chữ số nhị phân , có lẽ một tối ưu hóa các thuật toán ngây thơ mà xen kẽ chữ số thập phân, như các của C-vuông .

  • sự xen kẽ nhị phân dẫn đến chiến lược chỉ số đường cong Z một cách tự nhiên, nhà phát minh Geohash không bắt đầu "tìm đường cong fractal tốt nhất" ... Nhưng thật kỳ lạ, tối ưu hóa thiết kế này, đường cong fractal tốt hơn là có thể (!).

Sử dụng thư viện hình học S2

Cách tiếp cận hình học S2 là tham chiếu Geohash tốt hơn vì nó sử dụng cấu trúc liên kết hình cầu (khối lập phương), sử dụng phép chiếu tùy chọn (vì vậy tất cả các ô có hình dạng gần và diện tích gần nhau) và vì lập chỉ mục với đường cong Hilbert tốt hơn tham số Z- đường cong thứ tự :

... chúng ta có thể làm tốt hơn ... Sự gián đoạn khi chúng ta đi từ góc trên bên phải xuống dưới cùng bên trái khiến chúng ta phải chia ra một số phạm vi mà chúng ta có thể làm cho liên tục. (...) Chúng tôi có thể loại bỏ hoàn toàn bất kỳ sự gián đoạn nào (...)
blog.notdot.net/2009 về lập chỉ mục không gian với Quadtrees và Hilbert Curves

Bây giờ nó là một thư viện miễn phí và hiệu quả, xem https://s2geometry.io

PS: cũng có các phiên bản đơn giản hóa không chính thức (tốt) như NodeJSs2-geometry và nhiều "sân chơi", bổ trợ và trình diễn, như s2.sidewalklabs.com .


2

Tôi sẽ khuyên bạn nên sử dụng truy vấn GEORADIUS trong redis.

Đẩy dữ liệu được phân chia theo cấp độ geohash phù hợp nhất bằng cách sử dụng lệnh gọi GEOADD.

Ngoài ra, hãy xem cái này -> ProighborHash .

ProighborHash tạo ra một tập hợp các geohash bao phủ một vùng hình tròn, với các tọa độ trung tâm và bán kính. Nó cũng có một tùy chọn bổ sung để sử dụng GeoRaptor tạo ra sự kết hợp tốt nhất của geohash qua các cấp khác nhau để thể hiện vòng tròn, bắt đầu từ cấp cao nhất và lặp lại cho đến khi pha trộn tối ưu. Độ chính xác của kết quả vẫn giống như mức geohash bắt đầu, nhưng kích thước dữ liệu giảm đáng kể, do đó cải thiện tốc độ và hiệu suất.

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.