サンプルコード

様々な言語にジオポコードの符号化と複合化を実装したサンプルコードです。申し訳ないのですが、十分なテストをしていませんので、間違いやバグなどが含まれているかもしれません、あくまで参考程度に利用してください。

また、効率のよいアルゴリズム、サンプルコードの修正、新しい言語のサンプルコードなどございましたら、是非ともご連絡ください。公開の許可がいただけましたら、こちらに掲載します。

掲載サンプルコード
  1. JavaScript
  2. PHP
  3. C++
  4. Objective-C
  5. Perl
  6. Ruby
  7. Java
  8. Python
  9. Haskell
  10. エクセル

1. JavaScript


/*
 * GeoPo Encode in JavaScript
 * @author : Shintaro Inagaki
 * @param location (Object)
 * @return geopo (String)
 */
function geopoEncode(location){
	// 64characters (number + big and small letter + hyphen + underscore)
	var chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";

	var geopo = new String();
	var lat = parseFloat(location.lat); // Parse as float
	var lng = parseFloat(location.lng); // Parse as float
	var scale = parseInt(location.scale); // Parse as int
	
	// Change a degree measure to a decimal number
	lat = (lat + 90) / 180 * Math.pow(8, 10);
	lng = (lng + 180) / 360 * Math.pow(8, 10);

	// Compute a GeoPo code from head and concatenate
	for(var i = 0; i < scale; i++) {
		geopo = geopo + chars.substr(Math.floor(lat / Math.pow(8, 9 - i) % 8) + Math.floor(lng / Math.pow(8, 9 - i) % 8) * 8, 1);
	}
	return geopo;
}		

/*
 * GeoPo Decode in JavaScript
 * @author : Shintaro Inagaki
 * @param geopo (String)
 * @return location (Object)
 */
function geopoDecode(geopo){
	// 64characters (number + big and small letter + hyphen + underscore)
	var chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";

	var location = new Object();
	var lat = 0;
	var lng = 0;
	var scale = geopo.length; // Scale is length of GeoPo code
	var order = 0;

	for (var i = 0; i < scale; i++) {
		// What number of character that equal to a GeoPo code (0-63)
		order = chars.indexOf(geopo.substr(i, 1));
		// Lat/Lng plus geolocation value of scale 
		lat = lat + Math.floor(order % 8) * Math.pow(8, 9 - i);
		lng = lng + Math.floor(order / 8) * Math.pow(8, 9 - i);
	}
	
	// Change a decimal number to a degree measure, and plus revised value that shift center of area
	location.lat = lat * 180 / Math.pow(8, 10) + 180 / Math.pow(8, scale) / 2 - 90;
	location.lng = lng * 360 / Math.pow(8, 10) + 360 / Math.pow(8, scale) / 2 - 180;
	location.scale = scale;

	return location;
}		

2. PHP


/*
 * GeoPo Encode in PHP
 * @author : Shintaro Inagaki
 * @param $location (Array)
 * @return $geopo (String)
 */
function geopoEncode($location) {
	// 64characters (number + big and small letter + hyphen + underscore)
	$chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";

	$geopo = "";
	$lat = $location['lat'];
	$lng = $location['lng'];
	$scale = $location['scale'];
	
	// Change a degree measure to a decimal number
	$lat = ($lat + 90) / 180 * pow(8, 10);
	$lng = ($lng + 180) / 360 * pow(8, 10);

	// Compute a GeoPo code from head and concatenate
	for($i = 0; $i < $scale; $i++) {
		$geopo .= substr($chars, floor($lat / pow(8, 9 - $i) % 8) + floor($lng / pow(8, 9 - $i) % 8) * 8, 1);
	}
	return $geopo;
}		

/*
 * GeoPo Decode in PHP
 * @author : Shintaro Inagaki
 * @param $geopo (String)
 * @return $location (Array)
 */
function geopoDecode($geopo) {
	// 64characters (number + big and small letter + hyphen + underscore)
	$chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
	// Array for geolocation
	$location = array ();

	for ($i = 0; $i < strlen($geopo); $i++) {
		// What number of character that equal to a GeoPo code (0-63)
		$order = strpos($chars, substr($geopo, $i, 1));
		// Lat/Lng plus geolocation value of scale 
		$location['lat'] = $location['lat'] + floor($order % 8) * pow(8, 9 - $i);
		$location['lng'] = $location['lng'] + floor($order / 8) * pow(8, 9 - $i);
	}

	// Change a decimal number to a degree measure, and plus revised value that shift center of area
	$location['lat'] = $location['lat'] * 180 / pow(8, 10) + 180 / pow(8, strlen($geopo)) / 2 - 90;
	$location['lng'] = $location['lng'] * 360 / pow(8, 10) + 360 / pow(8, strlen($geopo)) / 2 - 180;
	$location['scale'] = strlen($geopo);

	return $location;
}		

3. C++


/*
 * GeoPo Encode in C++
 * @author : Shintaro Inagaki
 * @param geopo, lat lng, scale (pointer)
 */
void geopoEncode(char *geopo, double &lat, double &lng, int &scale)
{
	// 64characters (number + big and small letter + hyphen + underscore)
	char chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
	int i;

	// Change a degree measure to a decimal number
	lat = (lat + 90) / 180 * pow(8.0, 10.0);
	lng = (lng + 180) / 360 * pow(8.0, 10.0);

	// Compute a GeoPo code from head and concatenate
	for(i = 0;i < scale;i++){
		geopo[i] = chars[(int)(floor(fmod(lat / pow(8.0, 9.0 - i), 8.0)) + floor(fmod(lng / pow(8.0, 9.0 - i), 8.0)) * 8)];
	}
	geopo[i] = '\0';
}		

/*
 * GeoPo Decode in C++
 * @author : Shintaro Inagaki
 * @param geopo, lat lng, scale (pointer)
 */
void geopoDecode(char *geopo, double &lat, double &lng, int &scale)
{
	// 64characters (number + big and small letter + hyphen + underscore)
	char chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
	int order, i;

	scale = strlen(geopo);

	for(i = 0;i < scale;i++){
		// What number of character that equal to a GeoPo code (0-63)
		order = (strchr(chars, geopo[i]) - chars);
		// Lat/Lng plus geolocation value of scale 
		lat = lat + fmod(order, 8.0) * pow(8.0, 9.0 - i);
		lng = lng + floor((double)order / 8.0) * pow(8.0, 9.0 - i);
	}
	// Change a decimal number to a degree measure, and plus revised value that shift center of area
	lat = lat * 180 / pow(8.0, 10.0) - 90 + 180 / pow(8.0, scale) / 2;
	lng = lng * 360 / pow(8.0, 10.0) - 180 + 360 / pow(8.0, scale) / 2;
}		

注意:標準ライブラリ stdio.h、math.h、string.h を使用。

4. Objective-C


/*
 * GeoPo Encode in Objective-C
 * @author : Shintaro Inagaki
 * @param geopo, latitude, longitude, scale (pointer)
 */
-(void)encodeGeopo:(id)geopo latitude:(double *)lat longitude:(double *)lng scale:(int *)scale {
	NSString *chars = @"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
	
	*lat = (*lat + 90) / 180 * pow(8.0, 10.0);
	*lng = (*lng + 180) / 360 * pow(8.0, 10.0);
	
	for(int i = 0; i < *scale; i++){
		[geopo appendString:[chars substringWithRange:NSMakeRange(floor(fmod(*lat / pow(8.0, 9.0 - i), 8.0)) + floor(fmod(*lng / pow(8.0, 9.0 - i), 8.0)) * 8, 1)]];
	}
}		

/*
 * GeoPo Decode in Objective-C
 * @author : Shintaro Inagaki
 * @param geopo, latitude, longitude, scale (pointer)
 */
-(void)decodeGeopo:(id)geopo latitude:(double *)lat longitude:(double *)lng scale:(int *)scale {
	NSString *chars = @"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
	int order;

	*scale = [geopo length];
	
	for (int i = 0; i < *scale; i++) {
		order = NSMaxRange([chars rangeOfString:[geopo substringWithRange:NSMakeRange(i, 1)]]) - 1;
		*lat += (order % 8) * pow(8, 9 - i);
		*lng += floor(order / 8) * pow(8, 9 - i);
	}
	
	*lat = *lat * 180 / pow(8, 10) + 180 / pow(8, *scale) / 2 - 90;
	*lng = *lng * 360 / pow(8, 10) + 360 / pow(8, *scale) / 2 - 180;
}		

5. Perl


#
# GeoPo Encode in Perl
# @author : Shintaro Inagaki
# @param %location (Hash) [lat (Float), lng (Float), scale(Int)]
# @return $geopo (String)
#
sub geopoEncode(%location) {
	# 64characters (number + big and small letter + hyphen + underscore)
	$chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";

	$geopo = "";
	$lat = $location{'lat'};
	$lng = $location{'lng'};
	$scale = $location{'scale'};

	# Change a degree measure to a decimal number
	$lat = ($lat + 90) / 180 * 8 ** 10;
	$lng = ($lng + 180) / 360 * 8 ** 10;

	# Compute a GeoPo code from head and concatenate
	for($i = 0; $i < $scale; $i++) {
		$geopo .= substr($chars, int($lat / 8 ** (9 - $i) % 8) + int($lng / 8 ** (9 - $i) % 8) * 8, 1);
	}
	return $geopo;
}		

#
# GeoPo Decode in Perl
# @author : Shintaro Inagaki
# @param $geopo (String)
# @return %location (Hash) [lat (Float), lng (Float), scale(Int)]
#
sub geopoDecode($geopo) {
	# 64characters (number + big and small letter + hyphen + underscore)
	$chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
	# Hash for geolocation
	%location = ();

	for ($i = 0; $i < length($geopo); $i++) {
		# What number of character that equal to a GeoPo code (0-63)
		$order = index($chars, substr($geopo, $i, 1));
		# Lat/Lng plus geolocation value of scale 
		$location{'lat'} += $order % 8 * 8 ** (9 - $i);
		$location{'lng'} += int($order / 8) * 8 ** (9 - $i);
	}

	# Change a decimal number to a degree measure, and plus revised value that shift center of area
	$location{'lat'} = $location{'lat'} * 180 / 8 ** 10 - 90 + 180 / 8 ** length($geopo) / 2;
	$location{'lng'} = $location{'lng'} * 360 / 8 ** 10 - 180 + 360 / 8 ** length($geopo) / 2;
	$location{'scale'} = length($geopo);

	return %location;
}		

6. Ruby


#
# GeoPo Encode in Ruby
# @author : Shintaro Inagaki
# @param location (Hash) [lat (Float), lng (Float), scale(Int)]
# @return geopo (String)
#
def geopoEncode(location)
	# 64characters (number + big and small letter + hyphen + underscore)
	chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"
	
	geopo = ""
	lat = location['lat']
	lng = location['lng']
	scale = location['scale']
	
	# Change a degree measure to a decimal number
	lat = (lat + 90.0) / 180 * 8 ** 10; # 90.0 is forced FLOAT type when lat is INT
	lng = (lng + 180.0) / 360 * 8 ** 10; # 180.0 is same

	# Compute a GeoPo code from head and concatenate
	for i in 0..scale
		geopo += chars[(lat / 8 ** (9 - i) % 8).floor + (lng / 8 ** (9 - i) % 8).floor * 8, 1];
	end
	return geopo;
end		

#
# GeoPo Decode in Ruby
# @author : Shintaro Inagaki
# @param geopo (String)
# @return location (Hash) [lat (Float), lng (Float), scale(Int)]
#
def geopoDecode(geopo)
	# 64characters (number + big and small letter + hyphen + underscore)
	chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"
	# Hash for geolocation
	location = {}

	lat = 0.0
	lng = 0.0
	scale = geopo.size # Scale is length of GeoPo code
	order = 0

	for i in 0..(scale - 1)
		# What number of character that equal to a GeoPo code (0-63)
		order = chars.index(geopo[i])
		# Lat/Lng plus geolocation value of scale
		lat = lat + order % 8 * 8 ** (9 - i)
		lng = lng + (order / 8).floor * 8 ** (9 - i)
	end

	# Change a decimal number to a degree measure, and plus revised value that shift center of area
	location['lat'] = lat * 180 / 8 ** 10 - 90 + 180 / 8 ** scale / 2
	location['lng'] = lng * 360 / 8 ** 10 - 180 + 360 / 8 ** scale / 2
	location['scale'] = scale

	return location
end		

id:fawさんにRubyで実装していただきました。ありがとうございます。
http://d.hatena.ne.jp/faw/20090306/1236310192

7. Java


/**
 * GeoPo Encode in Java.
 *
 * @param lat latitude
 * @param lng longnitude
 * @param scale scale of map
 * @return geopo code
 */
public static String encode(double lat, double lng, int scale) {

    // 64characters (number + big and small letter + hyphen + underscore).
    final String chars =
            "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";

    StringBuffer geopo = new StringBuffer();

    lat = (lat + 90) / 180 * Math.pow(8, 10);
    lng = (lng + 180) / 360 * Math.pow(8, 10);

    for(int i = 0; i < scale; i++) {
        geopo.append(
                chars.charAt((int)(Math.floor(lat / Math.pow(8, 9 - i) % 8)
                        + Math.floor(lng / Math.pow(8, 9 - i) % 8) * 8)));
    }

    return geopo.toString();

}		

/**
 * GeoPo Decode in Java.
 *
 * @param geopo geopo code
 * @return latitude and longnitude
 */
public static double[] decode(String geopo) {

    // 64characters (number + big and small letter + hyphen + underscore).
    final String chars =
            "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";

    int scale = geopo.length();

    double[] loc = new double[2];
    loc[0] = 0;
    loc[1] = 0;

    for(int i = 0; i < scale; i++) {

        int order = chars.indexOf(geopo.charAt(i));

        loc[0] += Math.floor(order % 8) * Math.pow(8, 9 - i);
        loc[1] += Math.floor(order / 8) * Math.pow(8, 9 - i);

    }

    loc[0] = loc[0] * 180 / Math.pow(8, 10) - 90 + 180 / Math.pow(8, scale) / 2;
    loc[1] = loc[1] * 360 / Math.pow(8, 10) - 180 + 360 / Math.pow(8, scale) / 2;

    return loc;

}		

上記のコードはtakimuraさんにJavaで実装していただきました。ありがとうございます。

8. Python


#
# GeoPo Encode in Python
# @author : Shintaro Inagaki
# @param location (Dictionary) [lat (Float), lng (Float), scale(Int)]
# @return geopo (String)
#
def geopoEncode(location) :
       # 64characters (number + big and small letter + hyphen + underscore)
       chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"

       geopo = ""
       lat = location['lat']
       lng = location['lng']
       scale = location['scale']

       # Change a degree measure to a decimal number
       lat = (lat + 90.0) / 180 * 8 ** 10 # 90.0 is forced FLOAT type when lat is INT
       lng = (lng + 180.0) / 360 * 8 ** 10 # 180.0 is same

       # Compute a GeoPo code from head and concatenate
       for i in range(scale):
               order = int(lat / (8 ** (9 - i)) % 8) + int(lng / (8 ** (9 - i)) % 8) * 8
               geopo = geopo + chars[order]

       return geopo		

#
# GeoPo Decode in Python
# @author : Shintaro Inagaki
# @param geopo (String)
# @return location (Dictionary) [lat (Float), lng (Float), scale(Int)]
#
def geopoDecode(geopo) :
       # 64characters (number + big and small letter + hyphen + underscore)
       chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"

       lat = 0.0
       lng = 0.0
       scale = len(geopo) # Scale is length of GeoPo code
       order = 0

       for i in range(scale):
               # What number of character that equal to a GeoPo code (0-63)
               order = chars.find(geopo[i])
               # Lat/Lng plus geolocation value of scale
               lat = lat + order % 8 * 8 ** (9 - i)
               lng = lng + int(order / 8) * 8 ** (9 - i)

       # Change a decimal number to a degree measure, and plus revised value
that shift center of area
       location['lat'] = lat * 180 / 8 ** 10 - 90 + 180 / 8 ** scale / 2
       location['lng'] = lng * 360 / 8 ** 10 - 180 + 360 / 8 ** scale / 2
       location['scale'] = scale

       return location		

id:fawさんにPythonで実装していただきました。ありがとうございます。
http://d.hatena.ne.jp/faw/20090306/1236306272

10. エクセル


ジオポのエンコード方法(エクセル関数)

以下で利用している【】はセルの位置です(A1、B4等)。各自読み替えて利用してください。

緯度を入力するセルの位置を【緯度】とします。
経度を入力するセルの位置を【経度】とします。
符号化に使用する文字列のあるセルを【符号】とし、
「0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_」
を入力します(「」は除いてください)。

ジオポコードの1文字目を出力したいセルに、
「=MID(【符号】,INT(MOD((【緯度】+90)/180*POWER(8,10)/POWER(8,9),8))+INT(MOD((【経度】+180)/360*POWER(8,10)/POWER(8,9),8))*8+1,1)」
とすると、1文字目のジオポコードが得られます。
同様に、
「=MID(【符号】,INT(MOD((【緯度】+90)/180*POWER(8,10)/POWER(8,8),8))+INT(MOD((【経度】+180)/360*POWER(8,10)/POWER(8,8),8))*8+1,1)」
とPOWERの2番目の引数を1引いてやると、2文字目のジオポコードとなり、2文字目以降も同じ繰り返しとなります。

それぞれのジオポコードのセルを&で結合してやれば、ジオポコードの文字列となります。

(例)
A1に緯度、B1に経度、C1に符号化文字列を入力。
A2に「=MID(C1,INT(MOD((A1+90)/180*POWER(8,10)/POWER(8,9),8))+INT(MOD((B1+180)/360*POWER(8,10)/POWER(8,9),8))*8+1,1)」と入力。
B2に「=MID(C1,INT(MOD((A1+90)/180*POWER(8,10)/POWER(8,8),8))+INT(MOD((B1+180)/360*POWER(8,10)/POWER(8,8),8))*8+1,1)」と入力。
C2に「=MID(C1,INT(MOD((A1+90)/180*POWER(8,10)/POWER(8,7),8))+INT(MOD((B1+180)/360*POWER(8,10)/POWER(8,7),8))*8+1,1)」と入力。
D2に「=MID(C1,INT(MOD((A1+90)/180*POWER(8,10)/POWER(8,6),8))+INT(MOD((B1+180)/360*POWER(8,10)/POWER(8,6),8))*8+1,1)」と入力。
E2に「=MID(C1,INT(MOD((A1+90)/180*POWER(8,10)/POWER(8,5),8))+INT(MOD((B1+180)/360*POWER(8,10)/POWER(8,5),8))*8+1,1)」と入力。
F2に「=MID(C1,INT(MOD((A1+90)/180*POWER(8,10)/POWER(8,4),8))+INT(MOD((B1+180)/360*POWER(8,10)/POWER(8,4),8))*8+1,1)」と入力。
A3に「=A2&B2&C2&D2&E2&F2」と入力すれば、A3で6文字のジオポコードが得られます。
		

ジオポのデコード方法(エクセル関数)

以下で利用している【】はセルの位置です(A1、B4等)。各自読み替えて利用してください。

ジオポコードを入力するセルの位置を【ジオポ】とします。
符号化に使用する文字列のあるセルを【符号】とし、
「0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_」
を入力します(「」は除いてください)。
また、便宜的に縮尺精度は6文字とします。

緯度を出力したいセルに、
「=MOD(FIND(MID(【ジオポ】,1,1),【符号】)-1,8)*POWER(8,9)+MOD(FIND(MID(【ジオポ】,2,1),【符号】)-1,8)*POWER(8,8)+MOD(FIND(MID(【ジオポ】,3,1),【符号】)-1,8)*POWER(8,7)+MOD(FIND(MID(【ジオポ】,4,1),【符号】)-1,8)*POWER(8,6)+MOD(FIND(MID(【ジオポ】,5,1),【符号】)-1,8)*POWER(8,5)+MOD(FIND(MID(【ジオポ】,6,1),【符号】)-1,8)*POWER(8,4)」
とすると、緯度が得られます。
経度を出力したいセルに、
「=(INT((FIND(MID(【ジオポ】,1,1),【符号】)-1)/8)*POWER(8,9)+INT((FIND(MID(【ジオポ】,2,1),【符号】)-1)/8)*POWER(8,8)+INT((FIND(MID(【ジオポ】,3,1),【符号】)-1)/8)*POWER(8,7)+INT((FIND(MID(【ジオポ】,4,1),【符号】)-1)/8)*POWER(8,6)+INT((FIND(MID(【ジオポ】,5,1),【符号】)-1)/8)*POWER(8,5)+INT((FIND(MID(【ジオポ】,6,1),【符号】)-1)/8)*POWER(8,4))*360/POWER(8,10)+360/POWER(8,6)/2-180」
とすると、経度が得られます。

(例)
A3に6文字のジオポコード、C1に符号化文字列を入力。
A1に「=(MOD(FIND(MID(A3,1,1),C1)-1,8)*POWER(8,9)+MOD(FIND(MID(A3,2,1),C1)-1,8)*POWER(8,8)+MOD(FIND(MID(A3,3,1),C1)-1,8)*POWER(8,7)+MOD(FIND(MID(A3,4,1),C1)-1,8)*POWER(8,6)+MOD(FIND(MID(A3,5,1),C1)-1,8)*POWER(8,5)+MOD(FIND(MID(A3,6,1),C1)-1,8)*POWER(8,4))*180/POWER(8,10)+180/POWER(8,6)/2-90」
B1に「=(INT((FIND(MID(A3,1,1),C1)-1)/8)*POWER(8,9)+INT((FIND(MID(A3,2,1),C1)-1)/8)*POWER(8,8)+INT((FIND(MID(A3,3,1),C1)-1)/8)*POWER(8,7)+INT((FIND(MID(A3,4,1),C1)-1)/8)*POWER(8,6)+INT((FIND(MID(A3,5,1),C1)-1)/8)*POWER(8,5)+INT((FIND(MID(A3,6,1),C1)-1)/8)*POWER(8,4))*360/POWER(8,10)+360/POWER(8,6)/2-180」
と入力すれば、A1で緯度、B1で経度が得られます。