GeoHexのPHPポーティング (http://github.com/geohex/geohex-php) の
世界対応版(v2.01)で作成しました。
元になるjsがアップデートするとのことですのでまだお試し版です。
問題等ありましたら教えていただけると助かります。
2010/09/11追記
特定の値でloc2zoneがちょっとずれるバグがあるようです。
geohex2_coreのリファクタリングを取り込みつつ調査しています。
<?php
/* GeoHex module for php */
/* DISCLAIMER OF WARRANTY
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
NECESSARY SERVICING, REPAIR, OR CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
*/
/*
GeoHex oliginaly written by @sa2da (http://geogames.net/, http://geohex.net/) in Javascript
Ported to PHP by Mage Whopper at 2010.9.8
Copyright (C) 2009 sa2da (http://twitter.com/sa2da)
License : CC-BY-SA-2.1-JP
You can find the full legal code at
http://creativecommons.org/licenses/by/2.1/jp/legalcode (Japanese)
or in the local file cc-by-sa-2.1-legalcode.html.
such like http://creativecommons.org/licenses/by-sa/3.0/legalcode (English)
Here is only an abstract :
You are free:
to Share – to copy, distribute and transmit the work
to Remix – to adapt the work
Under the following conditions:
Attribution – You must attribute the work in the manner specified by the author or licensor
(but not in any way that suggests that they endorse you or your use of the work).
Share Alike — If you alter, transform, or build upon this work, you may distribute the
resulting work only under the same or similar license to this one.
For any reuse or distribution, you must make clear to others
the license terms of this work. The best way to do this
is with a link to this
(http://creativecommons.org/licenses/by-sa/3.0/) web page.
Any of the above conditions can be waived if you get
permission from the copyright holder.
Nothing in this license impairs or restricts the author's moral rights.
SPECIAL THANKS TO @_hfu_ for projection transform mathematical expression
(http://twitter.com/_hfu_/status/23144088252)
*/
class GeoHex{
var $h_key = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var $h_base = 20037508.34;
var $h_deg;
var $h_k;
var $h_size;
var $h_x;
var $h_y;
var $h_lon;
var $h_lat;
function __construct(){
$this->h_deg = pi()*(30/180);
$this->h_k = tan($this->h_deg);
}
// this function only for downward compatibility
function geohex2latlng( $code ){
return $ths->getZoneByCode($code);
}
// this function only for downward compatibility
function latlng2geohex($lat, $lon, $level){
return $ths->getZoneByLocation($lat, $lon, $level);
}
function setHexSize($_level){
if($_level < 1 || $_level > 24){
return -1;
}
return $this->h_base/pow(2,$_level)/3;
}
function getZoneByLocation( $_lat, $_lon, $_level){
$this->h_size = $this->setHexSize($_level);
if($this->h_size < 0){
die("invalid level error!");
}
$level = $_level;
list($lon_grid, $lat_grid) = $this->loc2xy($_lon,$_lat);
$unit_x = 6* $this->h_size;
$unit_y = $unit_x*$this->h_k;
$h_pos_x = ($lon_grid + $lat_grid/$this->h_k)/$unit_x;
$h_pos_y = ($lat_grid - $this->h_k*$lon_grid)/$unit_y;
$h_x_0 = floor($h_pos_x);
$h_y_0 = floor($h_pos_y);
$h_x_q = floor(($h_pos_x - $h_x_0)*100)/100;
$h_y_q = floor(($h_pos_y - $h_y_0)*100)/100;
$this->h_x = round($h_pos_x);
$this->h_y = round($h_pos_y);
$h_max=round($this->h_base/$unit_x + $this->h_base/$unit_y);
if($h_y_q>-$h_x_q+1){
if(($h_y_q<2*$h_x_q)&&($h_y_q>0.5*$h_x_q)){
$this->h_x = $h_x_0 + 1;
$this->h_y = $h_y_0 + 1;
}
}else if($h_y_q<-$h_x_q+1){
if(($h_y_q>(2*h_x_q)-1)&&($h_y_q<(0.5*$h_x_q)+0.5)){
$this->h_x = $h_x_0;
$this->h_y = $h_y_0;
}
}
$this->h_lat = ($this->h_k*$this->h_x*$unit_x + $this->h_y*$unit_y)/2;
$this->h_lon = ($this->h_lat - $this->h_y*$unit_y)/$this->h_k;
list($z_loc_x, $z_loc_y) = $this->xy2loc($this->h_lon,$this->h_lat);
if(($this->h_base - $this->h_lon) < $this->h_size){
$z_loc_x = 180;
$h_xy = $this->h_x;
$this->h_x = $this->h_y;
$this->h_y = $h_xy;
}
$h_x_p =0;
$h_y_p =0;
if($this->h_x<0){ $h_x_p = 1;}
if($this->h_y<0){ $h_y_p = 1;}
$h_x_abs = abs($this->h_x)*2+$h_x_p;
$h_y_abs = abs($this->h_y)*2+$h_y_p;
$h_x_10000 = floor(($h_x_abs%77600000)/1296000);
$h_x_1000 = floor(($h_x_abs%1296000)/216000);
$h_x_100 = floor(($h_x_abs%216000)/3600);
$h_x_10 = floor(($h_x_abs%3600)/60);
$h_x_1 = floor(($h_x_abs%3600)%60);
$h_y_10000 = floor(($h_y_abs%77600000)/1296000);
$h_y_1000 = floor(($h_y_abs%1296000)/216000);
$h_y_100 = floor(($h_y_abs%216000)/3600);
$h_y_10 = floor(($h_y_abs%3600)/60);
$h_y_1 = floor(($h_y_abs%3600)%60);
$hl_key = $this->h_key;
$h_code = "" . substr($hl_key, ($level%60), 1);
if($h_max >=1296000/2) $h_code = $h_code . $hl_key[$h_x_10000] . $hl_key[$h_y_10000];
if($h_max >=216000/2) $h_code = $h_code . $hl_key[$h_x_1000] . $hl_key[$h_y_1000];
if($h_max >=3600/2) $h_code = $h_code . $hl_key[$h_x_100] . $hl_key[$h_y_100];
if($h_max >=60/2) $h_code = $h_code . $hl_key[$h_x_10] . $hl_key[$h_y_10];
$h_code = $h_code . $hl_key[$h_x_1] . $hl_key[$h_y_1];
return array(
"lat" => $z_loc_y,
"lon" => $z_loc_x,
"x" => $this->h_x,
"y" => $this->h_y,
"code" => $h_code
);
}
function getZoneByCode($_code){
$c_length = strlen($_code);
$zone = array();
$hl_key=$this->h_key;
$level = strpos( $hl_key, $_code[0]);
$scl = $level;
$this->h_size = $this->h_base/pow(2,$level)/3;
$unit_x = 6* $this->h_size;
$unit_y = 6* $this->h_size* $this->h_k;
$h_max=round($this->h_base/$unit_x + $this->h_base/$unit_y);
$this->h_x=0;
$this->h_y=0;
if( $h_max >= (1296000/2)) {
$this->h_x = strpos( $hl_key, $_code[1]) *1296000+strpos( $hl_key, $_code[3] )*216000+strpos( $hl_key, $_code[5] )*3600+strpos( $hl_key, $_code[7] )*60+strpos( $hl_key, $_code[9] );
$this->h_y = strpos( $hl_key, $_code[2] )*1296000+strpos( $hl_key, $_code[4] )*216000+strpos( $hl_key, $_code[6] )*3600+strpos( $hl_key, $_code[8] )*60+strpos( $hl_key, $_code[10] );
}else if($h_max >=(216000/2) ) {
$this->h_x = strpos( $hl_key, $_code[1] )*216000+strpos( $hl_key, $_code[3] )*3600+strpos( $hl_key, $_code[5] )*60+strpos( $hl_key, $_code[7] );
$this->h_y = strpos( $hl_key, $_code[2] )*216000+strpos( $hl_key, $_code[4] )*3600+strpos( $hl_key, $_code[6] )*60+strpos( $hl_key, $_code[8] );
}else if($h_max >=3600/2){
$this->h_x = strpos( $hl_key, $_code[1] )*3600+strpos( $hl_key, $_code[3] )*60+strpos( $hl_key, $_code[5] );
$this->h_y = strpos( $hl_key, $_code[2] )*3600+strpos( $hl_key, $_code[4] )*60+strpos( $hl_key, $_code[6] );
}else if(h_max >=60/2){
$this->h_x = strpos( $hl_key, $_code[1] )*60+strpos( $hl_key, $_code[3] );
$this->h_y = strpos( $hl_key, $_code[2] )*60+strpos( $hl_key, $_code[4] );
}else{
$this->h_x = strpos( $hl_key, $_code[1] );
$this->h_y = strpos( $hl_key, $_code[2] );
}
$this->h_x = ($this->h_x%2)?-($this->h_x-1)/2:$this->h_x/2;
$this->h_y = ($this->h_y%2)?-($this->h_y-1)/2:$this->h_y/2;
$h_lat_y = ($this->h_k*$this->h_x*$unit_x + $this->h_y*$unit_y)/2;
$h_lon_x = ($h_lat_y - $this->h_y*$unit_y)/$this->h_k;
list($this->h_lon, $this->h_lat ) = $this->xy2loc($h_lon_x,$h_lat_y);
return array(
"code" => $_code,
"lat" => $this->h_lat,
"lon" => $this->h_lon,
"x" => $this->h_x,
"y" => $this->h_y
);
}
function loc2xy($_lon, $_lat) {
$x = $_lon*$this->h_base/180;
$y = log(tan((90+$_lat)*pi()/360))/(pi()/180);
$y = $y*$this->h_base/180;
return array($x, $y);
}
function xy2loc($_x, $_y) {
$lon=($_x/$this->h_base)*180;
$lat=($_y/$this->h_base)*180;
$lat=180/pi()*(2*atan(exp($lat*pi()/180))-pi()/2);
return array($lon, $lat);
}
function _gh_strpos( $num ){
$hl_key = $this->h_key;
return strpos($hl_key, $_code[$num]);
}
}/* class geohex */
使い方
//モジュールの読み込み
require_once('geohex2.01.php');
// GeoHexオブジェクト生成
$ghx = &new GeoHex();
// 各計算
// 緯度経度からGeoHexコードを求める
print $ghx->getZoneByLocation($latitude, $longitude, $level) ."\n";
// GeoHexコードから緯度経度に変換
print $ghx->getZoneByCode($geohex)."\n";