我的数据库中有一些坐标,可以通过使用GEO函数过滤最近的位置来检索。
但是它不够精确,因为它不是行驶距离。
我已经使用HERE Maps API来获取路由信息,但是加载整个列表并在此之后调用API是如此繁重。
是否有任何方法可以将Doctrine和HERE API结合使用或以其他方式对地点进行排序或过滤?
谢谢
大约一年前,我在一个类似的项目中遇到了这个确切的问题。唯一的区别是,我使用了Google的Distance Matrix API来确定实际的行驶里程。
简短的答案是:,您不能在查询级别执行此操作,因为数据库不具备路由信息的知识。 (也不能提供这种信息。)即使您确实构建了一些抢先式路由信息,您仍然需要查询每个实体以找到其位置来做到这一点,此外,您还将对maps API施加巨大压力,这可能会导致您快速达到使用配额(并为此付费)!
如果您要查询的实体存储坐标,您可以实际获得的最近距离首先是按照与更可行的集合(例如50)的直接距离对数据进行过滤/排序。然后在您的控制器中使用maps API通过行驶距离对结果数组进行排序。这样可以减少查询地图API所需的时间。
您可以通过以下示例查询直接距离:
public function findClostestTo(float $latitude, float $longitude, float $milesLimit = null, int $resultsLimit = 1): ?array
{
$qb = $this->createQueryBuilder("entity")
->addSelect("DEGREES(ACOS((SIN(RADIANS(:latitude)) * SIN(RADIANS(entity.latitude))) + (COS(RADIANS(:latitude)) * COS(RADIANS(entity.latitude)) * COS(RADIANS(:longitude - entity.longitude))))) * :radius AS distanceMiles")
// ->addSelect("(distanceMiles * 1.609344) AS distanceKilometres")
// ->addSelect("(distanceMiles * 0.868976) AS distanceNauticalMiles")
->setParameter("latitude", $latitude)
->setParameter("longitude", $longitude)
->setParameter("radius", (60 * 1.1515))
->addOrderBy("distanceMiles", "ASC")
;
if (is_numeric($milesLimit) && $milesLimit > 0.0) {
$qb
->andWhere("distanceMiles < :milesLimit")
->setParameter("milesLimit", $milesLimit)
;
}
$qb->setMaxResults(max(min($resultsLimit, 50), 1));
return $qb->getQuery()->getResult();
}
输入$latitude
和$longitude
作为您想计算距离的起点/终点,例如搜索用户的位置
请注意,此存储库函数中数组中的每个结果将不是每个实体的直接实例。它将分为2个键:
0
:目标实体实例distanceMiles
:用于限制距离并按距离排序的额外伪字段然后,您需要在控制器中迭代结果数组,以查询您的地图API并获取确切的行驶距离。我个人需要在drivingMiles
和0
上添加另一个键distanceMiles
,然后可以在usort()
中使用它们来对整个结果集进行排序,以构建最终结果集。
[在大多数情况下,这应该效果很好,但是,在某些边缘情况下,某些结果会超出初始距离限制,这是由于道路中怪异的弯道/方向环回源头而导致的。您仍然可以通过usort()
将它们从最终数组中过滤掉,如果发现最终结果集太小,则只需根据直接距离逐渐增加查询限制。