Laravel 教程:使用 MySQL 5.7 的 ST_Distance_Sphere 来计算距离

最近在一个客户项目中,我们必须使基于用户和数据库中实体之间的距离来过滤数据库查询结果变得容易。在这种情况下,很容易不知所措,担心您将不得不运行大量的 PHP 计算。

这不是唯一值得关注的项目。还有很多其他种类的应用程序,它们必须以某种方式,形状或形式来找到 A 点和 B 点之间的简单距离。

以下是一些其他可能需要此功能的应用程序:

  • 跑步或骑行距离追踪器;您可能首先需要找到 A 点与 B 点之间的距离。然后是 B 点与 C 点之间的距离。然后是 C 点与 D 点之间的距离。(这就是所谓的 "linestring", 但它仍然可以归结为简单的距离问题。)
  • 饭店送餐服务可能需要查看您是否在其最大送餐距离内。

我相信您还能想到更多。

MySQL可以做到!

如果您需要计算这一点,那么仅使用 MySQL 就能真正 出人意料地 做到!

MySQL 5.7 引入了 ST_Distance_Sphere, 这是一个计算两点之间距离的原生函数(在地球上).

像动物一样计算

以前,您可能必须手动实现类似 haversine formula 之类的工具才能进行此简单的测量。但是,此方法有两个缺点:

  • 您将必须编写/维护自己的程序
  • 比新的 ST_Distance_Sphere 函数要慢很多

测试

因此,让我们使用 ST_Distance_Sphere 来计算伊利诺伊州芝加哥的 Tighten 总部与纽约州纽约的 2017 Laracon 场地之间的距离!

我将地址插入 Google Maps,并使用其 “测量距离” 功能得到结果是 713.83 英里(就像鸟直飞一样)

Laravel

提示:您可以轻松地从 Google Maps URL 或 the "what's here" contextual menu 获得一个地点的坐标。

首先,我们需要 Tighten 总部的坐标:

纬度: 41.9631174, 经度: -87.6770458

接下来,我们需要 Laracon 场地的坐标:

纬度: 40.7628267, 经度: -73.9898293

然后把这些值带入!

注意:point 方法的参数首先是经度,然后是纬度;这是一个常见的陷阱!

select ST_Distance_Sphere(
    point(-87.6770458, 41.9631174),
    point(-73.9898293, 40.7628267)
)     

这使我们得到 1148978.6738241839,以米为单位,因此我们将其转换为英里:(1 米是 0.000621371192 英里)

select ST_Distance_Sphere(
    point(-87.6770458, 41.9631174),
    point(-73.9898293, 40.7628267)
) * .000621371192

这将返回 713.8304301984796,该距离在与谷歌地图 713.83 英里的舍入距离之内。

将 MySQL 的此功能与浏览器定位服务结合使用,您无需任何外部 API 即可获得简单的距离!

Laravel 示例

这是一个使用 Laravel 查询生成器和 Artisan Tinker 显示两个点之间的距离的示例:

Laravel

和代码:

dd(\DB::select(\DB::raw('
    select ST_Distance_Sphere(
        point(:lonA, :latA),
        point(:lonB, :latB)
    ) * 0.00621371192
'), [
    'lonA' => -87.6770458,
    'latA' => 41.9631174,
    'lonB' => -73.9898293,
    'latB' => 40.7628267,
]));

这是一种基于与给定位置的接近度来选择结果的类似方法:

// Eloquent Scope:
public function scopeCloseTo(Builder $query, $latitude, $longitude)
{
    return $query->whereRaw("
       ST_Distance_Sphere(
            point(longitude, latitude),
            point(?, ?)
        ) * .000621371192 < delivery_max_range
    ", [
        $longitude,
        $latitude,
    ]);
}

// Using the scope:
return Restaurant::closeTo($myLatitude, $myLongitude); 

注意事项和限制

但是,如果我不提及此方法的局限性,我将不知所措:

  • 正如你已经学习到的,这只是“鸟儿直飞”的距离。 如果您需要考虑道路路线或交通的距离,这种方法将无济于事。
  • 这些 MySQL functions default to using SRID 0, 对于基本用途而言是 接近足够 了, 但是如果高保真度精度很重要,则您将需要匹配用例。下面更多。

什么是SRID?

SRID 基本上是从空间坐标转换为地球坐标的方法。默认情况下,MySQL使用SRID 0,它表示“无限平坦的笛卡尔平面,其轴未分配单位”。Google和Bing使用SRID 3857,它是“球形墨卡托投影坐标系”。 (注意:「Google 地球」使用 SRID 4326。)

但是,对于许多应用程序而言,ST_Distance_Sphere 足以构建所需的功能。

结论

如果您使用的是MySQL 5.7+,并且需要查找距离,请记住首先想到 ST_Distance_Sphere*。即使它不一定总是适合您的代码,这也是获取和查询地球表面真实距离的一种简单而强大的方法。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://tighten.co/blog/a-mysql-distance...

译文地址:https://learnku.com/laravel/t/40975

本帖已被设为精华帖!
本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 4

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!