geohash 附近搜索

原创
2018/03/15 17:21
阅读数 287

附近搜索 geohash.neighbors 生成周边8个geohash(如下图),生成的长方形区域,与我要的搜索附近半径几公里以内肯定不相符。找到老外写的 https://github.com/ashwin711/proximityhash

 

 

需要 php版本代码 @ 我

<?php
/**
 * author https://my.oschina.net/jszhang
 * copy2php  https://github.com/ashwin711/proximityhash 
 */

// demo 
// ProximityHash::create_geohash($lat, $lng, 1000, 6);

class ProximityHash
{
    public static function in_circle_check($latitude, $longitude, $centre_lat, $centre_lon, $radius)
    {

        $x_diff = $longitude - $centre_lon;
        $y_diff = $latitude - $centre_lat;

        if (pow($x_diff, 2) + pow($y_diff, 2) <= pow($radius, 2)) {
            return true;
        }

        return false;
    }

    public static function get_centroid($latitude, $longitude, $height, $width)
    {
        $y_cen = $latitude + ($height / 2);
        $x_cen = $longitude + ($width / 2);

        return [$x_cen, $y_cen];
    }

    public static function convert_to_latlon($y, $x, $latitude, $longitude)
    {

        $pi = 3.14159265359;

        $r_earth = 6371000;

        $lat_diff = ($y / $r_earth) * (180 / $pi);
        $lon_diff = ($x / $r_earth) * (180 / $pi) / cos($latitude * $pi / 180);

        $final_lat = $latitude + $lat_diff;
        $final_lon = $longitude + $lon_diff;

        return [$final_lat, $final_lon];

    }

    public static function create_geohash($latitude, $longitude, $radius, $precision, $georaptor_flag = false, $minlevel = 1, $maxlevel = 12)
    {
        $x = 0.0;
        $y = 0.0;

        $points    = [];
        $geohashes = [];

        $grid_width  = [5009400.0, 1252300.0, 156500.0, 39100.0, 4900.0, 1200.0, 152.9, 38.2, 4.8, 1.2, 0.149, 0.0370];
        $grid_height = [4992600.0, 624100.0, 156000.0, 19500.0, 4900.0, 609.4, 152.4, 19.0, 4.8, 0.595, 0.149, 0.0199];

        $height = ($grid_height[$precision - 1]) / 2;
        $width  = ($grid_width[$precision - 1]) / 2;

        $lat_moves = intval(ceil($radius / $height));
        $lon_moves = intval(ceil($radius / $width));

        foreach (range(0, $lat_moves) as $i) {

            $temp_lat = $y + $height * $i;

            foreach (range(0, $lon_moves) as $j) {

                $temp_lon = $x + $width * $j;

                if (self::in_circle_check($temp_lat, $temp_lon, $y, $x, $radius)) {

                    list($x_cen, $y_cen) = self::get_centroid($temp_lat, $temp_lon, $height, $width);

                    $points[] = self::convert_to_latlon($y_cen, $x_cen, $latitude, $longitude);
                    $points[] = self::convert_to_latlon(-$y_cen, $x_cen, $latitude, $longitude);
                    $points[] = self::convert_to_latlon($y_cen, -$x_cen, $latitude, $longitude);
                    $points[] = self::convert_to_latlon(-$y_cen, -$x_cen, $latitude, $longitude);
                }
            }
        }

        foreach ($points as $point) {
            $geohashes[] = GeoHash::encode($point[0], $point[1], $precision);
        }

        $geohashes = array_unique($geohashes);

        if ($georaptor_flag) {
            // GeoRaptor我没copy
            // return GeoRaptor::compress($geohashes, $minlevel, $maxlevel);
            return $geohashes;
        } else {
            return $geohashes;
        }

    }

}
<?php

class GeoHash
{
    const BASE32_CODES      = "0123456789bcdefghjkmnpqrstuvwxyz";
    const BASE32_CODES_DICT = ['0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'b' => 10, 'c' => 11, 'd' => 12, 'e' => 13, 'f' => 14, 'g' => 15, 'h' => 16, 'j' => 17, 'k' => 18, 'm' => 19, 'n' => 20, 'p' => 21, 'q' => 22, 'r' => 23, 's' => 24, 't' => 25, 'u' => 26, 'v' => 27, 'w' => 28, 'x' => 29, 'y' => 30, 'z' => 31];

    // const BASE32_CODES_DICT = array_flip(explode('', self::BASE32_CODES));
    /**
     * Significant Figure Hash Length
     *
     * This is a quick and dirty lookup to figure out how long our hash
     * should be in order to guarantee a certain amount of trailing
     * significant figures. This was calculated by determining the error:
     * 45/2^(n-1) where n is the number of bits for a latitude or
     * longitude. Key is # of desired sig figs, value is minimum length of
     * the geohash.
     * @type Array
     */
    //     Desired sig figs:  0  1  2  3  4   5   6   7   8   9  10
    const SIGFIG_HASH_LENGTH = [0, 5, 7, 8, 11, 12, 13, 15, 16, 17, 18];

    public static function encode($latitude, $longitude, $numberOfChars = '')
    {
        if ($numberOfChars === 'auto') {
            $decSigFigsLat   = strlen(strstr($latitude, '.')) - 1;
            $decSigFigsLong  = strlen(strstr($longitude, '.')) - 1;
            $numberOfSigFigs = max($decSigFigsLat, $decSigFigsLong);
            $numberOfChars   = self::SIGFIG_HASH_LENGTH[$numberOfSigFigs];
        } else if (empty($numberOfChars)) {
            $numberOfChars = 9;
        }

        $chars      = [];
        $bits       = 0;
        $bitsTotal  = 0;
        $hash_value = 0;
        $maxLat     = 90;
        $minLat     = -90;
        $maxLon     = 180;
        $minLon     = -180;
        $mid        = 0;

        while (count($chars) < $numberOfChars) {
            if ($bitsTotal % 2 === 0) {
                $mid = ($maxLon + $minLon) / 2;
                if ($longitude > $mid) {
                    $hash_value = ($hash_value << 1) + 1;
                    $minLon     = $mid;
                } else {
                    $hash_value = ($hash_value << 1) + 0;
                    $maxLon     = $mid;
                }
            } else {
                $mid = ($maxLat + $minLat) / 2;
                if ($latitude > $mid) {
                    $hash_value = ($hash_value << 1) + 1;
                    $minLat     = $mid;
                } else {
                    $hash_value = ($hash_value << 1) + 0;
                    $maxLat     = $mid;
                }
            }

            $bits++;
            $bitsTotal++;
            if ($bits === 5) {
                $chars[]    = self::BASE32_CODES[$hash_value];
                $bits       = 0;
                $hash_value = 0;
            }
        }

        return implode('', $chars);
    }

    public static function decode($hash_string)
    {
        $bbox   = self::decode_bbox($hash_string);
        $lat    = ($bbox[0] + $bbox[2]) / 2;
        $lon    = ($bbox[1] + $bbox[3]) / 2;
        $latErr = $bbox[2] - $lat;
        $lonErr = $bbox[3] - $lon;
        return [
            'latitude'  => $lat,
            'longitude' => $lon,
            'error'     => ['latitude' => $latErr, 'longitude' => $lonErr],
        ];
    }

    public static function decode_bbox($hash_string)
    {
        $isLon  = true;
        $maxLat = 90;
        $minLat = -90;
        $maxLon = 180;
        $minLon = -180;
        $mid;

        $hashValue = 0;
        for ($i = 0, $l = strlen($hash_string); $i < $l; $i++) {
            $code      = $hash_string[$i];
            $hashValue = self::BASE32_CODES_DICT[$code];

            for ($bits = 4; $bits >= 0; $bits--) {
                $bit = ($hashValue >> $bits) & 1;
                if ($isLon) {
                    $mid = ($maxLon + $minLon) / 2;
                    if ($bit === 1) {
                        $minLon = $mid;
                    } else {
                        $maxLon = $mid;
                    }
                } else {
                    $mid = ($maxLat + $minLat) / 2;
                    if ($bit === 1) {
                        $minLat = $mid;
                    } else {
                        $maxLat = $mid;
                    }
                }
                $isLon = !$isLon;
            }
        }
        return [$minLat, $minLon, $maxLat, $maxLon];
    }

    /**
     * Neighbors
     *
     * Returns all neighbors' hashstrings clockwise from north around to northwest
     * 7 0 1
     * 6 x 2
     * 5 4 3
     * @param {String} hash_string
     * @returns {encoded neighborHashList|Array}
     */
    public static function neighbors($hash_string)
    {

        $hashstringLength = strlen($hash_string);

        $lonlat = self::decode($hash_string);
        $lat    = $lonlat['latitude'];
        $lon    = $lonlat['longitude'];
        $latErr = $lonlat['error']['latitude'] * 2;
        $lonErr = $lonlat['error']['longitude'] * 2;

        $List = [
            [1, 0],
            [1, 1],
            [0, 1],
            [-1, 1],
            [-1, 0],
            [-1, -1],
            [0, -1],
            [1, -1],
        ];

        $neighborHashList = array_map(function ($neighbor) use ($lat, $lon, $latErr, $lonErr, $hashstringLength) {
            $neighbor_lat = $lat + $neighbor[0] * $latErr;
            $neighbor_lon = $lon + $neighbor[1] * $lonErr;
            return self::encode($neighbor_lat, $neighbor_lon, $hashstringLength);
        }, $List);

        return $neighborHashList;
    }

    public function get_box($arr)
    {
        $minLat = $arr[0][1];
        $minLon = $arr[0][0];
        $maxLat = $arr[0][1];
        $maxLon = $arr[0][0];

        foreach ($arr as $point) {
            $minLat = $point[1] > $minLat ? $minLat : $point[1];
            $minLon = $point[0] > $minLon ? $minLon : $point[0];
            $maxLat = $point[1] < $maxLat ? $maxLat : $point[1];
            $maxLon = $point[0] < $maxLon ? $maxLon : $point[0];
        }

        return [$minLat, $minLon, $maxLat, $maxLon];
    }
}

 

展开阅读全文
打赏
0
1 收藏
分享
加载中
3Q~~
2019/01/29 12:35
回复
举报
定期马甲博主

引用来自“阿松7880”的评论

博主能否分享一下,感谢。
压缩的没写哈
2019/01/29 11:53
回复
举报
定期马甲博主

引用来自“阿松7880”的评论

非常不错,请问php版本在哪里下载?
好了 上传了 https://my.oschina.net/jszhang/blog/1635440
2019/01/29 11:53
回复
举报
博主能否分享一下,感谢。
2019/01/28 16:28
回复
举报
非常不错,请问php版本在哪里下载?
2019/01/28 16:22
回复
举报
更多评论
打赏
5 评论
1 收藏
0
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部