様々な言語にジオポコードの符号化と複合化を実装したサンプルコードです。申し訳ないのですが、十分なテストをしていませんので、間違いやバグなどが含まれているかもしれません、あくまで参考程度に利用してください。
また、効率のよいアルゴリズム、サンプルコードの修正、新しい言語のサンプルコードなどございましたら、是非ともご連絡ください。公開の許可がいただけましたら、こちらに掲載します。
掲載サンプルコード
- JavaScript
- PHP
- C++
- Objective-C
- Perl
- Ruby
- Java
- Python
- Haskell
- エクセル
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で経度が得られます。