| Top Page | プログラミング | メッシュコード |
Updated on 2010-07-13
以下の内容のファイルへのリンク (MESH.pm)。 「リンク先を保存」等でダウンロードして使用。
# MESH.pm
#
# メッシュコード計算パッケージ (for Perl)
#
# 2010-07-12 TAKENAKA, Akio
#
# use MESH; で、このファイルて定義した4つの関数を
# 読み込んでから使用する。
#
# meshcode_to_latlong, メッシュコードから緯度・経度へ
# latlong_to_meshcode, 緯度・経度からメッシュコードへ。
# dms_to_deg, 度・分・秒から度(小数点表示)へ。
# deg_to_dms, 度(小数点表示)から度・分・秒へ。
package MESH;
use Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(
meshcode_to_latlong
latlong_to_meshcode
dms_to_deg
deg_to_dms
);
use strict;
########################################
# メッシュコードを渡し、対応する緯度・経度を返す。
# 特に指定しなければメッシュの中央の座標を返す。
# メッシュコードは一次、ニ次、三次いずれも可。
#
# NW, NE, SW, SE を2つ目の引数として渡すと、それぞれ北西、北東、南西、南東の
# コーナーの座標を返す。
#
# 緯度・経度は度単位の小数。
#
# usage
# my $code_a = "55354251"; # 三次メッシュコード
# my ($lat_c, $long_c) = &MESH::meshcode_to_latlong($code_a); # 中央の緯度・経度
#
# my $code_b = "5535"; # 一次メッシュコード
# my ($lat_ne, $long_ne) = &MESH::meshcode_to_latlong($code_b), 'NE'); # 北東端
sub meshcode_to_latlong
{
my $code = shift @_;
my ($code12, $code34, $code5, $code6, $code7, $code8);
my ($lat_width, $long_width); # ひとつのメッシュの幅
if ($code =~ /^(\d\d)(\d\d)/) { # 一次メッシュ相当部分の取り出し
$code12 = $1;
$code34 = $2;
$lat_width = 2 / 3;
$long_width = 1;
}
else { # 最初に4つの数字が並んでいなければその場で終了
die("MESH::mesh_to_latlong() invalid meshcode $code");
}
if ($code =~ /^\d{4}(\d)(\d)/) { # 二次メッシュ相当部分の取り出し
$code5 = $1;
$code6 = $2;
$lat_width /= 8;
$long_width /= 8;
}
if ($code =~ /^\d{6}(\d)(\d)/) { # 三次メッシュ相当部分の取り出し
$code7 = $1;
$code8 = $2;
$lat_width /= 10;
$long_width /= 10;
}
my $loc = "C"; # 位置指定をしない場合、中央の座標を返す。
if (@_) { # 位置指定があれば取得。
$loc = shift @_;
}
unless ($loc =~ /^C|(NE)|(NW)|(SE)|(SW)$/) { # 不正が位置指定
die("MESH::mesh_to_latlong() invalid option $loc");
}
# 以下、南西コーナーの座標を求める。
my $lat = $code12 * 2 / 3; # 一次メッシュ
my $long = $code34 + 100;
if ($code5 ne '' && $code6 ne '') { # 二次メッシュ
$lat += ($code5 * 2 / 3) / 8;
$long += $code6 / 8;
}
if ($code7 ne '' && $code8 ne '') { # 三次メッシュ
$lat += ($code7 * 2 / 3) / 8 / 10;
$long += $code8 / 8 / 10;
}
if ($loc =~ /C/) { # 中央の座標
$lat += $lat_width / 2;
$long += $long_width / 2;
}
if ($loc =~ /N/) { # 北端の座標
$lat += $lat_width;
}
if ($loc =~ /E/) { # 東端の座標
$long += $long_width;
}
$lat = sprintf("%.8f", $lat); # 小数点以下8桁まで。
$long = sprintf("%.8f", $long);
return ($lat, $long);
}
##################
# 緯度・経度(度単位)を受けとり、これを含むメッシュのコードを返す
#
# 3番めの引数として 1,2,3 を渡せば、一次メッシュ、二次メッシュ、
# 三次メッシュコードを計算する。指定がなければ三次メッシュ、
#
# Usage;
# my $code_3 = &MESH::latlong_to_meshcode(35.3, 138.5) # 三次メッシュコード
# my $code_2 = &MESH::latlong_to_meshcode(35.3, 138.5, 2) # 二次メッシュコード
sub latlong_to_meshcode
{
my ($lat, $long, $order) = @_;
unless ($order) {
$order = 3;
}
unless ($order =~ /^[123]$/) {
die ("ESJ::latlong_to_mesh() invalid mesh order $order\n");
}
# print $lat, "\t", $long, "\t", $order, "\n";
my ($code12, $code34, $code5, $code6, $code7, $code8);
# Latitude
my $lat_in_min = $lat * 60;
$code12 = int($lat_in_min / 40);
my $lat_rest_in_min = $lat_in_min - $code12 * 40;
$code5 = int($lat_rest_in_min / 5 ); # 二次メッシュの1区画は緯度5分。
$lat_rest_in_min -= $code5 * 5;
$code7 = int($lat_rest_in_min / (5/10));
# Longitude
$code34 = int($long) - 100;
my $long_rest_in_deg = $long - int($long);
$code6 = int($long_rest_in_deg * 8);
$long_rest_in_deg -= $code6 / 8;
$code8 = int($long_rest_in_deg / (1/80) );
my $code = $code12 . $code34;
if ($order >= 2) {
$code = sprintf("%s%01d%01d", $code, $code5, $code6);
}
if ($order >= 3) {
$code = sprintf("%s%01d%01d", $code, $code7, $code8);
}
return $code;
}
##################
# 度、分、秒を受けとり、度の単位で小数点表示に変換して返す。
#
# Usage;
# my $lat = '138:25:02';
# my $deg = &MESH::dms_to_deg( split(/:/, $lat) );
sub dms_to_deg
{
my ($deg, $min, $sec) = @_;
my $in_deg = $deg + $min / 60 + $sec / 3600;
$in_deg = sprintf("%.6f", $in_deg); # 小数点以下6桁まで。
return $in_deg;
}
##################
# 度の単位の小数点表示を受けとり、 度、分、秒に変換して返す。
#
# Usage;
# my $lat = 138.415;
# my ($d, $m, $s) = &MESH::deg_to_dms(lat);
# pirnt join(':', $d, $m, $s);
sub deg_to_dms
{
my $in_deg = shift @_;
my $deg = int($in_deg);
my $rest_in_deg = $in_deg - $deg;
my $min = int( $rest_in_deg * 60 );
my $rest_in_min = $rest_in_deg * 60 - $min;
my $sec = $rest_in_min * 60;
$sec = sprintf("%.2f", $sec); # 小数点以下2桁まで。
# もし繰り上がってしまっていたら、その分の調整、
if ($sec >= 60) { # 秒が60を越えたら
$sec -= 60;
$min += 1;
}
if ($min == 60) { # 分が60 を越えたら
$min -= 60;
$deg += 1;
}
return ($deg, $min, $sec);
}
1;