专栏首页仙士可博客空间索引-geohash编码解码类

空间索引-geohash编码解码类

算法实现原理请看:http://www.php20.cn/article/125

<?php

/**
 * Created by PhpStorm.
 * User: tioncico
 * Date: 18-4-21
 * Time: 上午11:27
 */
class Geohash
{
    const  LATITUDE = 1;
    const LONGITUDE = 2;
    const BASE32 = array(
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm',
        'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
        'y', 'z');

    public static function encode($latitude = 0, $longitude = 0, $level = 11)
    {
        $latitude_str  = self::separate($latitude, '', 1, self::get_precision_level_num($level, self::LATITUDE), self::get_interval_value(self::LATITUDE));
        $longitude_str = self::separate($longitude, '', 1, self::get_precision_level_num($level, self::LONGITUDE), self::get_interval_value(self::LONGITUDE));
        return self::geohash_encode(self::combination($latitude_str, $longitude_str));
    }

    public static function decode($geohash)
    {
        $data = array();
        $combination_str = self::geohash_decode($geohash);
        $separate_str = self::de_combination($combination_str);
        $data[self::LATITUDE] = self::de_separate($separate_str[self::LATITUDE],1,self::get_interval_value(self::LATITUDE));
        $data[self::LONGITUDE] = self::de_separate($separate_str[self::LONGITUDE],1,self::get_interval_value(self::LONGITUDE));

        return $data;
    }


    /**
     * 编码
     */

    /**
     * @param float $num经度或纬度
     * @param string $str递归字符串
     * @param int $i 递归次数
     * @param int $max_separate_num递归总次数
     * @param array $data 区间值
     * @return string
     */
    public static function separate($num, $str = '', $i = 1, $max_separate_num = 20, $data = array('min' => -90, 'max' => 90))
    {
        $count   = ($data['max'] - $data['min']) / 2;
        $limit_0 = array(
            'min' => $data['min'],
            'max' => $data['min'] + $count
        );
        $limit_1 = array(
            'min' => $data['min'] + $count,
            'max' => $data['max']
        );
        $str     .= $num > $limit_1['min'] ? 1 : 0;
        if ($i >= $max_separate_num) {
            return $str;
        } else {
            return self::separate($num, $str, $i + 1, $max_separate_num, $num > $limit_1['min'] ? $limit_1 : $limit_0);
        }
    }

    /**
     * @param $latitude_str 纬度
     * @param $longitude_str 经度
     */
    public static function combination($latitude_str, $longitude_str)
    {
        $str = '';
        for ($i = 0; $i < strlen($longitude_str); $i++) {//根据精度表,可发现维度>=精度
            $str .= $longitude_str{$i};
            if(isset($latitude_str{$i})){
                $str .=  $latitude_str{$i};
            }
        }
        return $str;
    }

    public static function geohash_encode($str)
    {
        $str_arr    = str_split($str, 5);//按5位分割字符串
        $encode_str = '';
        foreach ($str_arr as $va) {
            $decimal    = bindec($va);
            $encode_str .= self::BASE32[$decimal];
        }
        return $encode_str;
    }
    /**
     * 编码
     */

    /**
     * 解码
     */
    public static  function geohash_decode($str)
    {
        //根据一位字符串进行切割
        $str_arr    = str_split($str, 1);
        $decode_str = '';
        $base32     = array_flip(self::BASE32);
        foreach ($str_arr as $va) {
            $decode_str .= str_pad(decbin($base32[$va]),5,'0',STR_PAD_LEFT);
        }
        return (string)$decode_str;

    }

    /**
     * 解码二进制组合
     * @param $str
     * @return array
     */
    public static function de_combination($str)
    {
        $latitude_str  = '';
        $longitude_str = '';
        //根据两位字符串切割
        $str_arr = str_split($str, 2);
        foreach ($str_arr as $va) {
            $longitude_str .= $va[0];
            if(isset($va[1])){//根据精度表,可发现维度>=精度
                $latitude_str  .= $va[1];
            }
        }
        return array(
            self::LATITUDE=>$latitude_str,
            self::LONGITUDE=>$longitude_str,
        );
    }

    /**
     * 解码二分区间
     * @param $str
     * @param string $i//执行次数
     * @param array $data、、区间
     */
    public static function de_separate($str,$i=1,$data = array('min' => -90, 'max' => 90)){
        $count   = ($data['max'] - $data['min']) / 2;
        $limit_0 = array(
            'min' => $data['min'],
            'max' => $data['min'] + $count
        );
        $limit_1 = array(
            'min' => $data['min'] + $count,
            'max' => $data['max']
        );
        if($str[$i-1]==0){
            $data = $limit_0;
        }else{
            $data = $limit_1;
        }

        if ($i >= strlen($str)) {
            return $data;
        } else {
            return self::de_separate($str, $i + 1, $data);
        }
    }

    /**
     * 解码
     */

    /**
     * 根据精度获取二分次数
     * @param $level
     * @param $type
     */
    public static function get_precision_level_num($level, $type = self::LATITUDE)
    {
        $precision = array(
            1  => array(
                self::LATITUDE  => 2,
                self::LONGITUDE => 3,
            ),
            2  => array(
                self::LATITUDE  => 5,
                self::LONGITUDE => 5,
            ),
            3  => array(
                self::LATITUDE  => 7,
                self::LONGITUDE => 8,
            ),
            4  => array(
                self::LATITUDE  => 10,
                self::LONGITUDE => 10,
            ),
            5  => array(
                self::LATITUDE  => 12,
                self::LONGITUDE => 13,
            ),
            6  => array(
                self::LATITUDE  => 15,
                self::LONGITUDE => 15,
            ),
            7  => array(
                self::LATITUDE  => 17,
                self::LONGITUDE => 18,
            ),
            8  => array(
                self::LATITUDE  => 20,
                self::LONGITUDE => 20,
            ),
            9  => array(
                self::LATITUDE  => 22,
                self::LONGITUDE => 23,
            ),
            10 => array(
                self::LATITUDE  => 25,
                self::LONGITUDE => 25,
            ),
            11 => array(
                self::LATITUDE  => 27,
                self::LONGITUDE => 28,
            ),
            12 => array(
                self::LATITUDE  => 30,
                self::LONGITUDE => 30,
            ),
        );
        return $precision[$level][$type];

    }

    /**
     * 获取区间
     * @param $type
     * @return mixed
     */
    public static function get_interval_value($type = self::LATITUDE)
    {
        $interval = array(
            self::LATITUDE  => array(
                'min' => -90,
                'max' => 90
            ),
            self::LONGITUDE => array(
                'min' => -180,
                'max' => 180
            ),
        );
        return $interval[$type];
    }
}

$geohash = Geohash::encode(24.88849, 118.6197800000);
echo $geohash . "\n";
var_dump(Geohash::decode($geohash));

输出:

本文为仙士可原创文章,转载无需和我联系,但请注明来自仙士可博客www.php20.cn

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • js格式化字符串自动补位

    PHP的sprintf()函数可以格式化字符串并且自动补位,而js是没有这个函数的,可以自己自定义一个

    仙士可
  • mysql格式化字符串生成订单号的方法

    本文为仙士可原创文章,转载无需和我联系,但请注明来自仙士可博客www.php20.cn

    仙士可
  • php多数组组合计算

    仙士可
  • Dart字符串判空

    NullPointerExp是无数java程序员都想消除的问题,OC里,nil对象调方法返回的是nil(这种做法,仁者见仁,智者见智);kotlin和swift...

    DSoon
  • 从C#到TypeScript - 变量

    从C#到TypeScript - 变量 TypeScript的变量声明和ES6差不多,相比之前主要是多了let和const 为什么不用var 不管是TypeSc...

    用户1147588
  • 课时39:类与对象:拾遗

    Python的特性其实还支持另外一种很流行的编程模式:Mixin.【扩展阅读】Mixin编程机制(https://fishc.com.cn/forum.php?...

    py3study
  • Foundation-String

    最近写完了Swift 3.0教程 ,在接下来这段时间,继续写Foundation 的教程,帮助大家更加深入,系统的学习Foundation 框架,可能会持续一段...

    酷走天涯
  • robot framework笔记(三):扩展SeleniumLibrary库 (自定义关键字)

    以下代码GitHub 版本库地址: https://github.com/blairwind/blog_rf

    free赖权华
  • Bert+seq2seq 周公解梦,看AI如何解析你的梦境?

    作者:saiwaiyanyu 链接:https://juejin.im/post/5dd9e07b51882572f00c4523

    统计学家
  • Nginx 基于客户端IP分析

    下面是 nginx_analysis_log3.py 部分代码,获取程序全部代码,请关注我的 YP小站 微信公众号并回复 nginx客户端IP分析

    YP小站

扫码关注云+社区

领取腾讯云代金券