IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> Redis6+PHP:实现根据经纬度计算出附近门店距离 -> 正文阅读

[PHP知识库]Redis6+PHP:实现根据经纬度计算出附近门店距离

一. 开始介绍: Redis GEO
1.Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增。

Redis GEO操作方法
geoadd添加地理位置的坐标
geopos获取地理位置的坐标
geodist计算两个位置之间的距离
georadius根据用户给定的经纬度坐标来获取指定范围内的地理位置集合
georadiusbymember根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合
geohash返回一个或多个位置对象的 geohash 值
2.geoadd
 geoadd 用于存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。

3.geopos
 geopos 用于从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。

4.geodist
 geodist 用于返回两个给定位置之间的距离。
最后一个距离单位参数说明:
m :米,默认单位。
km :千米。
mi :英里。
ft :英尺。

5.georadius
 georadius 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
m :米,默认单位。
km :千米。
mi :英里。
ft :英尺。
WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 
WITHCOORD: 将位置元素的经度和维度也一并返回。
WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
COUNT 限定返回的记录数。
ASC: 查找结果根据距离从近到远排序。
DESC: 查找结果根据从远到近排序。

二. Redis6 + PHP 实现附近门店思路

1.首先在项目后台创建店铺的时候,一定要接通百度地图api,返回相对输入店铺地址的经纬度,保存到数据库后才能实现,没有经纬度无法实现 本人用的是laravel框架~~
  • 百度地图api地址 : https://lbsyun.baidu.com/index.php?title=webapi

  • 接下来是测试数据,如果对读者们有帮助,可以在实际项目中更具自己的思路加强改进~

  • 如果之前创建店铺的时候保存了经纬度,这个数据可以在数据库中读取,id是门店的id,longitude 经度,latitude纬度

  • 如果仔细的读者应该发现了,在这里用的语法是上文介绍过的,在这里只是把名称换成了店铺id,后面比较好和门店进行绑定

  • 可以看到redis库中已经有我们填写的测试数据了,接下来我们结合上文所说到的georadius方法进行取值

  • 这里使用的是asc升序排序,还有desc降序排序,一般附近门店第一个显示的是最近门店,所以用asc升序排序,单位是km 比较好查询

  • 显示的是km单位,把数据拿到后可以 *1000 改成m单位,更具自己的项目需求进行改变

  • 在这里遍历用店铺id做为数组的key

  • 在这将门店距离数据和门店信息数据组合

  • 最后组合成的数据,已经可以在前端小程序中渲染附近门店距离啦,这只是测试数据哦,真实数据要更具自己项目数据进行结合 对读者有帮助的话 帮我点个赞哦 期待留言~~

  • 成品效果哦~~

(ps: 最后可以设防,读取数据库门店信息条数和redis 储存的门店经纬度数据总数 进行对比,如果一致,则直接读取redis数据,否则重新查询数据库,将数据重新赋值给redis,可以减少数据库io查询)

代码块
//模拟门店 id 经度 纬度 
    $store_location = array(
        0 => array(
            'id' => 1,
            'longitude' => '113.956843',
            'latitude'  => '22.575094',
        ),
        1 => array(
            'id' => 2,
            'longitude' => '113.957746',
            'latitude'  => '22.574877',
        ),
        2 => array(
            'id' => 3,
            'longitude' => '113.957036',
            'latitude'  => '22.575545',
        ),
    );

    //遍历测试数据 循环在redis 插入 经纬度
    foreach ($store_location as $K => $item) {
        Redis::GeoAdd('store_location', $item['longitude'], $item['latitude'], $item['id']);
    }
    
    //在这里经纬度是写死的,在实际项目中这里传进来的经纬度是更具用户授权定位信息之后拿到的经纬度 
    $positions = Redis::GeoRadius('store_location', 113.955964, 22.574741, 1000, 'km', 'WITHDIST', 'asc');

    //在这里遍历用店铺id做为数组key
    $positions_container = [];
    foreach ($positions as $k => $v) {
        $positions_container[$v[0]] = $v;
    }
    
    //这里是门店信息测试数据,读者可以更具项目数据进行读取
    $stores = array(
        0 => array(
            'id'   => 1,
            'name' => 'ck',
        ),
        1 => array(
            'id'   => 2,
            'name' => 'myj',
        ),
        2 => array(
            'id'   => 3,
            'name' => 'tyb',
        ),
    );
    //门店信息容器
    $store_info = [];
    foreach ($stores as $k => $v) {  //遍历门店信息数据
        foreach ($positions_container as $kk => $vv) {  //遍历相对距离位置

            //如果数组key等于门店信息id 则把数据重新赋值给门店信息容器 store_info
            if ($kk = $v['id']) {
                $store_info[$kk]['id']    = $v['id'];
                $store_info[$kk]['name']  = $v['name'];
                $store_info[$kk]['apart'] = ($positions_container[$kk][1] * 1) * 1000;
            }
        }
    }
    //取出门店距离数组列
    $column = array_column($store_info, 'apart');
    //更具距离进行升序排序
    array_multisort($column, SORT_ASC, $store_info);
    dd($store_info);
    /**
     * 获取附近线下门店列表
     * @Author ZzzWClock
     * @param $longitude    经度
     * @param $latitude     纬度
     * @method get
     * @return json
     */
    public function queryOfflineStore()
    {   
        $longitude = req::get('longitude');
        $latitude  = req::get('latitude');

        // $longitude = '113.955964';
        // $latitude  = '22.574741';

        // 判断用户是否授权开启定位
        if (empty($longitude) && empty($latitude)) {
            return $this->resFailed(400, '请授权开启定位');
        }

        try {
            $offline_store_info = offlineStore::checkPosition($longitude, $latitude);
            return $this->resSuccess($offline_store_info);
        } catch (\Exception $e) {
            return $this->resFailed(400, $e->getMessage());
        }
        
    }
    /**
     * 根据经纬度获取附近门店
     * @Author ZzzWClock
     * @param  $longitude    经度
     * @param  $latitude     纬度
     * @param  $data         分页/筛选数据
     * @return array
     */
    public static function checkPosition(String $longitude, String $latitude, array $data = [])
    {
        
        // 线下门店信息
        $offline_store = self::queryOfflineStoreInfo($data);
        // 判断是否有线下门店
        if (!empty($offline_store)) {
            // 线下门店经纬度 添加进redis
            foreach ($offline_store as $key => $items) {
                Redis::GeoAdd('offline_location', $items['latitude'], $items['longitude'], $items['id']);
            }
            // 用户经纬度放入redis中计算距离
            $offline_location = Redis::GeoRadius('offline_location', $longitude, $latitude, 10000, 'km', 'WITHDIST', 'asc');
            // 遍历门店根据距离进行升序排序
            $store_info       = self::eachOfflineLocation($offline_location, $offline_store);

            return $store_info;
        } else {
            return $offline_store;
        }
        
    }
    
    /**
     * 查询线下门店信息
     * @Author ZzzWClock
     * @param  $data     分页/筛选数据
     * @return array
     */
    private static function queryOfflineStoreInfo(array $data = [])
    {
        // 分页/筛选数据
        // $data['page']     = $data['page']     ? $data['page'] : 1;
        // $data['pageSize'] = $data['pageSize'] ? $data['pageSize'] : 10;
        // 线下门店信息
        $offline_store = DB::table('shop_real_stores')
                            ->select([
                              'id',                     // 线下店铺id
                              'name',                   // 店铺名称
                              'brief',                  // 店铺描述
                              'mes',                    // 店铺信息
                              'province',               // 省
                              'city',                   // 市
                              'area',                   // 区
                              'street',                 // 街道
                              'longitude',              // 经度
                              'latitude',               // 纬度
                              'distribution',           // 配送距离
                              'business_hours',         // 营业时间
                              'address',                // 门店地址
                              'phone',                  // 门店电话
                              'image_url',              // 图片地址
                              'longitude',              // 经度
                              'latitude',               // 纬度
                            ])
                            ->where(['status' => 1])
                            ->lists();
        return $offline_store;
    }
    
    /**
     * 遍历线下门店经纬度索引 返回附近门店距离
     * @Author ZzzWClock
     * @param $offline_location    redis门店距离
     * @param $offline_store_info  线下门店信息
     * @return array
     */
    private static function eachOfflineLocation(array $param, array $offline_store_info)
    {
        // 遍历 redis 门店距离 更新索引
        $positions_container = [];
        foreach ($param as $k => $v) {
            $positions_container[$v[0]] = $v;
        }
        // 遍历 门店信息
        $store_info = [];
        foreach ($offline_store_info as $k => $v) {
            //二级遍历门店距离 通过key 找到相对应门店
            foreach ($positions_container as $kk => $vv) {
                if ($kk = $v['id']) {
                    $store_info[$k]['id']             = $v['id'];
                    $store_info[$k]['name']           = $v['name'];
                    $store_info[$k]['brief']          = $v['brief'];
                    $store_info[$k]['mes']            = $v['mes'];
                    $store_info[$k]['province']       = $v['province'];
                    $store_info[$k]['city']           = $v['city'];
                    $store_info[$k]['area']           = $v['area'];
                    $store_info[$k]['street']         = $v['street'];
                    $store_info[$k]['distribution']   = $v['distribution'];
                    $store_info[$k]['business_hours'] = json_decode($v['business_hours']);
                    $store_info[$k]['address']        = $v['address'];
                    $store_info[$k]['phone']          = $v['phone'];
                    $store_info[$k]['image_url']      = explode(',', $v['image_url']);
                    $store_info[$k]['apart']          = ($positions_container[$kk][1] * 1) * 1000;
                    $store_info[$k]['score']          = '5.0';
                    $store_info[$k]['longitude']      = $v['longitude'];
                    $store_info[$k]['latitude']       = $v['latitude'];
                }
            }
        }
        // 取出门店距离列
        $apart_column = array_column($store_info, 'apart');
        // 根据门店距离进行升序排序
        array_multisort($apart_column, SORT_ASC, $store_info);
        // 获取门店评分
        $store_info = self::queryStoreRemark($store_info);
        return $store_info;
    }
    
    /**
     * 获取门店评分
     * @Author ZzzWClock
     * @param  $offline_store_info  线下门店信息
     * @return array
     */
    private static function queryStoreRemark (array $offline_store_info)
    {
        // 获取门店id
        $offline_store_ids = array_column($offline_store_info, 'id');
        // 查询线下门店评分表
        $store_remark = DB::table('shop_offline_store_remark')
                            ->select([
                                'id',
                                'offline_store_id',
                                'total_score',
                            ])
                            ->where(['is_show' => 0])
                            ->whereIn('id', $offline_store_ids)
                            ->lists();
        
        if (!empty($store_remark)) {
            // 遍历线下门店信息 children 为评论数量
            foreach ($offline_store_info as $k => $store_info) {
                $offline_store_info[$k]['children'] = [];
                foreach ($store_remark as $kk => $remark) {
                    
                    if ($remark['offline_store_id'] == $store_info['id']) {
                        $offline_store_info[$k]['children'][] = $remark; 
                    }
                }
            }
            // 声明score_num 储存门店评分数量
            $score_num = [];
            foreach ($offline_store_info as $k => $store_info) {
                $score_num[$store_info['id']][] = count($store_info['children']);
            }
            // 遍历线下门店信息评分 = 每个children 评分总合 / 门店评分数量
            foreach ($offline_store_info as $k => $store_info) {
                $total_score = 0;
                foreach ($store_info['children'] as $kk => $cld) {
                    foreach ($score_num as $kkk => $num) {
                        
                        if ($kkk == $cld['offline_store_id']) {
                            $offline_store_info[$k]['score'] = ($total_score += $cld['total_score']) / $num[0];
                            
                        }
                    }
                }
                unset($offline_store_info[$k]['children']);
            }
            return $offline_store_info;
        } else {
            return $offline_store_info;
        }
    }
  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-10-24 14:45:00  更:2021-10-24 14:45:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 13:52:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计