忍者ブログ
  • 2018.09
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 2018.11
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

【2018/10/18 12:31 】 |
Geohash関数
Geohashエンコード、デコード関数を書いてみた。
Geohash文字列を緯度と経度の区間に変換:geohash_decode_interval
Geohash文字列を緯度と経度に変換:geohash_decode
緯度と経度をGeohash文字列に変換:geohash_encode
Geohash文字列から、隣接する領域((2n+1)^2-1個)のGeohash文字列を返す:geohash_neighbour

//integer to base32
function int_2_base32($v_in){
    $v = (int)$v_in;
    if($v < 0 || $v > 31) return false;

    $base32 = '0123456789bcdefghjkmnpqrstuvwxyz';
    $str = substr($base32, $v, 1);
    return $str;
}

//base32 to integer
function base32_2_int($str_in){
    if(strlen($str_in) != 1) return false;
    $str = $str_in;

    $base32 = '0123456789bcdefghjkmnpqrstuvwxyz';
    $v = strpos($base32, $str);
    return $v; // not found: false
}

//geohash string to intervals of latitude, longitude
function geohash_decode_interval($geohash_in){
    $geohash = $geohash_in;

    $ary_lat = array(-90.0, 90.0);
    $ary_lon = array(-180.0, 180.0);
    $lat_r = 90.0;
    $lon_r = 180.0;

    $len = strlen($geohash);
    if($len == 0) return false; //error

    $ary_mask = array(16,8,4,2,1);

    $is_even = true;
    for($i=0; $i<$len; $i++){
        $str = substr($geohash, $i, 1);
        $v = base32_2_int($str);
        if($v === false) return false; //error

        foreach($ary_mask as $mask){
            if($is_even){
                $lon_r /= 2;
                if($v & $mask){
                    $ary_lon = array(($ary_lon[0] + $ary_lon[1])/2, $ary_lon[1]);
                }
                else{
                    $ary_lon = array($ary_lon[0], ($ary_lon[0] + $ary_lon[1])/2);
                }
            }
            else{
                $lat_r /= 2;
                if($v & $mask){
                    $ary_lat = array(($ary_lat[0] + $ary_lat[1])/2, $ary_lat[1]);
                }
                else{
                    $ary_lat = array($ary_lat[0], ($ary_lat[0] + $ary_lat[1])/2);
                }
            }
            $is_even = !$is_even;
        }
    }

    return array($ary_lat, $ary_lon);
}

//geohash string to latitude, longitude
function geohash_decode($geohash_in){
    $ary_tmp = geohash_decode_interval($geohash_in);
    if($ary_tmp == false) return false;
    list($ary_lat, $ary_lon) = $ary_tmp;

    $places_lat = max(1, -round(log10($ary_lat[1] - $ary_lat[0]))) - 1;
    $places_lon = max(1, -round(log10($ary_lon[1] - $ary_lon[0]))) - 1;

    $lat = round(($ary_lat[0] + $ary_lat[1]) / 2, $places_lat);
    $lon = round(($ary_lon[0] + $ary_lon[1]) / 2, $places_lon);

    return array($lat, $lon);
}

//latitude, longitude to geohash string
function geohash_encode($lat_in, $lon_in, $len_in=11){
    $lat = (float)$lat_in;
    $lon = (float)$lon_in;
    if($lat < -90 || $lat > 90 || $lon < -180 || $lon > 180) return false;

    $len = (int)$len_in;
    if($len <= 0) return false;

    $ary_lat = array(-90.0, 90.0);
    $ary_lon = array(-180.0, 180.0);

    $cnt = 0;
    $str_bin = "";
    while($cnt <= $len * 5){
        $lon_c = ($ary_lon[0] + $ary_lon[1]) / 2;
        if($lon < $lon_c){
            $str_bin .= "0";
            $ary_lon[1] = $lon_c;
        }
        else{
            $str_bin .= "1";
            $ary_lon[0] = $lon_c;
        }
        $lat_c = ($ary_lat[0] + $ary_lat[1]) / 2;

        if($lat < $lat_c){
            $str_bin .= "0";
            $ary_lat[1] = $lat_c;
        }
        else{
            $str_bin .= "1";
            $ary_lat[0] = $lat_c;
        }
        $cnt++;
    }

    $str_geohash = "";
    for($i=0; $i<$len; $i++){
        $str_sub = substr($str_bin, $i*5, 5);
        $str_geohash .= int_2_base32(bindec($str_sub));
    }

    return $str_geohash;
}

//return geohash strings of neighbours
function geohash_neighbour($geohash_in, $range_in=1){
    $geohash = $geohash_in;
    $len = strlen($geohash);

    $range = (int)$range_in;
    if($range < 1) return false; //error

    $ary_tmp = geohash_decode_interval($geohash);
    if($ary_tmp == false) return false; //error

    list($ary_lat, $ary_lon) = $ary_tmp;
    $delta_lat = $ary_lat[1] - $ary_lat[0];
    $delta_lon = $ary_lon[1] - $ary_lon[0];
    $lat = ($ary_lat[0] + $ary_lat[1]) / 2;
    $lon = ($ary_lon[0] + $ary_lon[1]) / 2;

    $ary_geohash = array();
    for($i=-1*$range; $i<=1*$range; $i++){
        for($j=-1*$range; $j<=1*$range; $j++){
            if($i == 0 && $j == 0) continue;

            $lat_tmp = $lat + $delta_lat * $i;
            if($lat_tmp < -90.0) $lat_tmp += 180.0;
            else if($lat_tmp > 90.0) $lat_tmp -= 180.0;
            $lon_tmp = $lon + $delta_lon * $j;
            if($lon_tmp < -180.0) $lon_tmp += 360.0;
            else if($lon_tmp > 180.0) $lon_tmp -= 360.0;

            $str_tmp = geohash_encode($lat_tmp, $lon_tmp, $len);
            if($str_tmp == false) return false; //error
            $ary_geohash[] = $str_tmp;
        }
    }

    return $ary_geohash;
}

参考:
http://blog.masuidrive.jp/index.php/2010/01/13/geohash/trackback/
http://en.wikipedia.org/wiki/Geohash

ベタベタな書き方ですが・・・


PR
【2010/01/14 14:07 】 | 関数、ライブラリ | 有り難いご意見(0) | トラックバック(0)
| ホーム |