# # メッシュコード計算パッケージ (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;