如何使用Laravel / PHP根据经度/纬度接近度对对象进行分组

时间:2018-09-10 22:51:26

标签: php laravel-5 geolocation

我有一组用户。用户数可能是50,也可能是2000。每个人都应该有一个我从Google Geo api中检索到的长/宽。

我需要全部查询它们,并按接近度和一定数量对它们进行分组。假设计数为12,并且该组中有120个用户。我想按人们与他人的相距(远/近)分组。这样我就结识了10个距离很近的人。

我目前拥有Google地理编码api设置,并且希望使用它。

TIA。

-更新 我已经对此进行了一段时间的搜索,看来我正在寻找一个按接近度返回组的空间查询。

3 个答案:

答案 0 :(得分:5)

...似乎我正在寻找一个空间查询,该查询按接近程度返回分组。 ...

您可以使用hdbscan。您的组实际上是hdbscan措辞中的群集。您需要使用min_cluster_size和min_samples才能正确设置组。

https://hdbscan.readthedocs.io/en/latest/parameter_selection.html

https://hdbscan.readthedocs.io/en/latest/

看来hdbscan在Python下运行。

以下是有关如何从PHP调用Python的两个链接: Calling Python in PHPRunning a Python script from PHP

以下是有关选择哪种聚类算法的更多信息: http://nbviewer.jupyter.org/github/scikit-learn-contrib/hdbscan/blob/master/notebooks/Comparing%20Clustering%20Algorithms.ipynb

http://scikit-learn.org/stable/modules/clustering.html#clustering

答案 1 :(得分:5)

请记住,这个问题随着您添加的每个用户成倍增长,因为距离计算量与用户数的平方相关(实际上是N*(N-1)距离...因此有2000个用户群则意味着每趟行程将进行近400万次距离计算。在确定所需资源的大小时请记住这一点

您是要根据直线(实际上是一个大圆)距离还是根据步行/行车距离对它们进行分组?

如果是前者,那么如果您能够忍受很小的误差范围并且希望假设地球是一个球体,则可以用简单的数学方法估算出较大的圆距离。从GCMAP.com:

  

地球的假想形状称为大地水准面,近似为   椭圆形或扁球形。一个更简单的模型是使用   球体,它非常接近并且使数学更容易。假设   半径6371.2公里的球体,将经度和纬度转换为   弧度(乘以pi / 180),然后使用以下公式:

theta = lon2 - lon1
dist = acos(sin(lat1) × sin(lat2) + cos(lat1) × cos(lat2) × cos(theta))
if (dist < 0) dist = dist + pi
dist = dist × 6371.2
  

结果距离以公里为单位。

现在,如果您需要精确的计算并且愿意花费很多复杂数学所需的CPU周期,则可以使用Vincenty公式,该公式使用地球的WGS-84参考椭球模型进行导航,映射和没什么。更多信息HERE

对于算法本身,您需要使用每次计算的结果来构建一个“至”矩阵。每行和每列将代表每个节点。您可以考虑以下两种简化方法:

  1. 距离不取决于行进方向,因此$dist[n][m] == $dist[m][n](无需计算整个矩阵,只需计算一半即可)
  2. 从节点到其自身的距离始终为0,因此无需计算,但由于您打算按接近度进行分组,为避免用户与其自身分组,您可能希望始终强制{{1} }定义为任意定义且异常大的常量(例如$dist[m][m]。只要您的所有用户都在地球上,该常量就可以工作)

进行所有计算后,使用数组排序方法找到每个节点附近的X个最近节点。 (您可能会或可能不想阻止一个用户分组到多个组中,但这只是业务逻辑)

当前提供的实际代码太多了,没有先看到您的一些进展,但这基本上是您需要通过算法进行的工作。

答案 2 :(得分:0)

使用GeoHash算法[1]。有一个PHP实现[2]。您可以以不同的精度预先计算出地理哈希值,将它们与经纬度值一起存储在SQL数据库中,并使用本机GROUP BY进行查询。

  1. https://en.wikipedia.org/wiki/Geohash
  2. https://github.com/lvht/geohash