基于PHP+MongoDB的LBS附近的人应用-初探

写这篇文章是基于兴趣,早之前好奇一些社交APP、O2O应用可能会带有“附近的人”类似的功能,到底他们是如何做的呢?下面我简单的分析下用到的技术,小学生的分析欢迎批评指正。在线体验:http://182.92.217.161/map/index.php (H5+PHP+Mongo 请使用 手机打开,最好是在非WIFI 环境下,那样定位更加准确)。


基于 GeoHash + (B Tree + R tree 储存,最好是 R tree)技术,详细关于GeoHash深度技术可阅读以下文章:
http://www.cnblogs.com/LBSer/p/3310455.html
http://iamzhongyong.iteye.com/blog/1399333


依旧沿用简单粗暴的解决问题的方案,直接使用空间索引的 DB,这里我就折中选择 MongoDB,提供空间索引的DB 很多,例如 Redis 3.2 以上,MongoDB 、PostgreSQL、MySQL,关于这几款的性能测评,详情参照: http://www.cnblogs.com/zhenbianshu/p/6817569.html.


上代码套餐:


ini_set("display_errors", "On");
error_reporting(E_ALL | E_STRICT);
$mongo_config = [
    'host'=>'127.0.0.1',
    'port'=>27017,
    'dbname'=>'my_app',
    'user'=>'',
    'pwd'=>''
];
$host_port = $mongo_config['host'] . ":" . $mongo_config['port'];
try {
    $mongoDB = new MongoClient($host_port);
    $mongo_model = $mongoDB->selectDB($mongo_config['dbname']);
} catch(\Exception $e) {
    echo responseJson(0, 'MongoDB connection was fail.');
    exit();
}
$lbs = new LBS();
$lbs->setKvDB($mongo_model);

$method = trim($_POST['method']);
switch($method) {
    case 'join':
        $longitude = $_POST['lng'];
        $latitude = $_POST['lat'];
        $uid = $_POST['uuid'];
        $r = $lbs->geoAdd($longitude, $latitude, $uid);
        if($r) {
            $users = $lbs->geoSearch($longitude, $latitude);
            if(!empty($users)) {
                echo responseJson(1, '已经找到附近的小伙伴了.', $users);
            } else {
                echo responseJson(0, '你附近没有小伙伴.');
            }
        } else {
            echo responseJson(0, '上报地理位置失败');
            exit();
        }
        break;
    case 'search':
        echo georadiusbymember($redis);
        break;
    default:
        echo responseJson(0,'未知操作');
        break;
}

class LBS
{
    private $kvDB;
    private $index_type = '2dsphere';
    private $table_name = 'user_lbs';

    /**
     * 设置储存媒介,限定 mongoDB,所有操作基于mongo
     * @param object $mongoDB
     *
     */
    public function setKvDB(MongoDB $mongoDB) {
        $this->kvDB = $mongoDB;
    }

    /**
     * 设置 lbs 表名称
     * @param double $longitude 经度
     * @param double $latitude  维度
     * @param string/int $uid   用户ID
     * @param array $data  		其他数据,k v 形式, example: $data = ['username' => 'kevin','geo' => 'center'];
     */
    public function geoAdd($longitude, $latitude, $uid, $data = []) {
        $d = [
            'loc' => [
                'type' => 'Point',
                'coordinates' => [doubleval($longitude), doubleval($latitude)]
            ],
            'uid' => $uid
        ];
        if($this->checkData($data)) {
            $d = array_merge($d, $data);
        }
        $collection = $this->kvDB->selectCollection($this->table_name);
        //查询 该uid 是否存在,存在则更新
        //$collection->remove(['uid' => $uid], ['justOne' => true]);      // test
        $exist = $collection->findOne(['uid' => $uid]);
        $collection->ensureIndex(['loc' => $this->index_type]);
        if($exist) {
            $r = $collection->update(['uid' => $uid], ['$set' => $d]);
        } else {
            $r = $collection->insert($d);
        }
        return (isset($r['ok']) && !empty($r['ok'])) ? true : false;
    }

    /**
     * 根据 经纬度查询附近人
     * @param double $longitude 经度
     * @param double $latitude  维度
     * @param int    $maxdistance   默认 2000 Mi(米)
     * @param int    $limit        默认拉取100
     * @return array 附近的人集合
     */
    public function geoSearch($longitude, $latitude, $maxdistance = 1000, $limit = 100) {
        $coll = $this->kvDB->selectCollection($this->table_name);
        $r = $this->kvDB->command(
            [
                'geoNear' => $this->table_name,
                'near' => [ 'type' => 'Point','coordinates' =>[doubleval($longitude), doubleval($latitude)]],
                'spherical' => true,
                'maxDistance' => $maxdistance,
                'num' => $limit,
            ]
        );
        if(isset($r['ok']) && !empty($r['ok'])) {
            return $r['results'];
        } else {
            return false;
        }
    }

    /**
     * 安全监测 如需严格,则需要判断经纬度在范围
     * @param array $data
     * @return bool|array
     */
    public function checkData($data) {

        if(empty($data)) return false;
        if(isset($data['loc'])) unset($data['loc']);
        if(isset($data['uid'])) unset($data['uid']);
        return $data;
    }
    /**
     * 设置 lbs 表名称
     * @param string $table_name default value "user_lbs"
     *
     */
    public function setTableName($table_name) {
        $this->table_name = $table_name;
    }
    /**
     * 获取 lbs 表名称
     * @return string table name
     */
    public function getTableName()
    {
        return $this->table_name;
    }

    /**
     * 返回json
     * @param int    $state    状态
     * @param string $message   消息
     * @param array  $data     数据
     * @return json string      json 数据
     */
    function responseJson($state = 0, $message = '', $data = null) {
        $r['state'] = $state;
        $r['message'] = $message;
        if(!is_null($data)) {
            $r['data'] = $data;
        }
        return json_encode($r);
    }
}

/**
 * 返回json
 * @param int    $state    状态
 * @param string $message   消息
 * @param array  $data     数据
 * @return json string      json 数据
 */
function responseJson($state = 0, $message = '', $data = null) {
    $r['state'] = $state;
    $r['message'] = $message;
    if(!is_null($data)) {
        $r['data'] = $data;
    }
    return json_encode($r);
}

效果图
基于PHP+MongoDB的LBS附近的人应用-初探_第1张图片

你可能感兴趣的:(PHP,算法)