住所から緯度経度を取得する

提供: dococo wiki
ナビゲーションに移動 検索に移動

目的

国土交通省の街区レベル位置参照情報とauホームページの「実効速度について」の情報から、住所をキーとして緯度経度を抽出して、速度の中央値を算したいとおもいます

考え方の注意

今回のプログラムは、簡単に説明すれば2つのファイルの共通事項(キー:ここでは測定場所・住所)を比較して、合致(ヒット)したら、出力するという簡単な動作ですが、作り方を失敗すると途方も無い時間がかかるプログラムになります。
今回のサンプルでは

緯度経度の入っている街区レベル位置参照情報で26万レコード
auホームページの「実効速度についての測定ポイントが1500ヶ所

総当たりで、検索をした場合3.9億回ループを回すことになります。全最後までループが回るとは考えられませんが、それでも相当な時間がかかるプログラムになると予想できます
しかし総当たりしないと「ヒット」するか否か分かりません。従って時間短縮のためにどうするかを考えます

キーのメモリ読み込み

ディスクアクセスに相当な時間がかかるので、キーとなる情報をメモリ内に読み込ませてループを回します。そうすることでかなりの時間が短縮されます。
また、応用としてキーとなる情報を事前に出来るだけ減らしてメモリ内に読み込ませれば、さらに時間短縮なります。

プログラム

自分の勉強用に、いろいろ理解しながら書いてます
動作内容を丁寧にコメントアウトしていますので、ソースを見ながら理解してください。


文字列操作

宮崎県宮崎市佐土原町下田島 付近 (1) 付近 (1)が邪魔なので、Perlによる指定文字列以降削除の確認プログラム
結果としては

入力文字:宮崎県宮崎市佐土原町下田島 付近 (1)
出力文字:宮崎県宮崎市佐土原町下田島

となる

#ソースファイルは UTF8形式で保存
use strict;
use warnings;
#内部処理はUTF-8で行う
use utf8;
use Encode;
my $moji1 = '宮崎県宮崎市佐土原町下田島 付近 (1)';
$moji1 =~ s/ 付近.*$//;

#Windows7への出力はShift-JISで(こうしないと文字化けする)
#最後の*はスペースが入っていない事を確認するための印
print Encode::encode('shiftjis',$moji1.'*' );

連続文字列操作

引数で、CSV形式のデータを読み込んで付近(1)という文字を全て消すためのプログラム
CSVイメージは以下の通り

計測日時 計測場所 計測端末 NW種別 下り平均速度 上り平均速度
2015/12/01 12時台 宮崎県宮崎市島之内 付近 (1) iPhone 6s LTE 33Mbps 7Mbps
2015/12/01 13時台 宮崎県宮崎市島之内 付近 (2) iPhone 6s LTE 67Mbps 12Mbps
2015/12/01 13時台 宮崎県宮崎市島之内 付近 (3) iPhone 6s LTE 79Mbps 10Mbps
2015/12/01 13時台 宮崎県宮崎市島之内 付近 (4) iPhone 6s LTE 48Mbps 10Mbps
2015/12/01 14時台 宮崎県宮崎市島之内 付近 (5) iPhone 6s LTE 78Mbps 12Mbps
2015/12/02 12時台 宮崎県宮崎市新名爪 付近 (1) iPhone 6s LTE 60Mbps 12Mbps
2015/12/02 12時台 宮崎県宮崎市新名爪 付近 (2) iPhone 6s LTE 47Mbps 8Mbps
2015/12/02 13時台 宮崎県宮崎市新名爪 付近 (3) iPhone 6s LTE 69Mbps 12Mbps
2015/12/02 13時台 宮崎県宮崎市新名爪 付近 (4) iPhone 6s LTE 64Mbps 12Mbps
#ソースファイルは UTF8形式で保存
	use strict;
	use warnings;
	
	#内部処理はUTF-8で行うための宣言
	use utf8;
	use Encode;

	#コマンドライン引数の数が「@ARGV」に代入される
	#コマンドライン引数の数が「1」の場合では無いときはエラーを出して終了
	if (@ARGV != 1){
					print "ファイルを指定してください";
					exit;
					}
					
	#「$file_name」に引数を代入する
	#myはレキシカルスコープ変数。スコープ内のみで有効
	#この位置ではプログラム内全てがスコープの中に入っていると判断されるので
	#全ての位置で参照可能
	#
	my $file_name = $ARGV[0];		#コマンドライン引数が$file_nameに代入される

	#chompで末尾の改行文字を削除する
	chomp($file_name);
	
	#元のファイルは大抵Shift-JISのため、Windowsの文字コード(cp932)を指定する
	use open IN  => ":encoding(cp932)";
	
	#コマンドライン引数で指定したファイルを読込モードで開く
	open (FILE, "<$file_name") or die "ファイルがありません\n";
	
	#ファイルを1行づつ処理する
	while (my $line = <FILE>){
		
		#改行を削除する
		chomp($line);
		
		#カンマで行を分けて配列に格納
		#生ログの情報は
		#日付,計測場所,計測端末,NW種別,ダウンロード速度,アップロード速度を分解する
		my ($day, $location, $phone, $nw_type, $download, $upload )= split(/,/, $line, 6);
		print Encode::encode('shiftjis',$location."*\n");
		
		#付近という文字以降を削除
		$location =~ s/ 付近.*$//;
		print Encode::encode('shiftjis',$location."*\n" );
				
		#Windows7への出力はShift-JISで(こうしないと文字化けする)
		#最後の*はスペースが入っていない事を確認するための印
		#print Encode::encode('shiftjis',$location.'*' );
	}
	close FILE;

半角数字を漢数字に変換

半角数字を漢数字に変換の処理を入れた場合のプログラム
この処理によって以下のような内容になる
結果としては

入力文字:北海道札幌市中央区北八条西26丁目 付近
出力文字:北海道札幌市中央区北八条西二十六丁目

となる

#ソースファイルは UTF8形式で保存
	use strict;
	use warnings;
	
	#内部処理はUTF-8で行うための宣言
	use utf8;
	use Encode;

	#コマンドライン引数の数が「@ARGV」に代入される
	#コマンドライン引数の数が「1」の場合では無いときはエラーを出して終了
	if (@ARGV != 1){
					print "ファイルを指定してください";
					exit;
					}
					
	#「$file_name」に引数を代入する
	#myはレキシカルスコープ変数。スコープ内のみで有効
	#この位置ではプログラム内全てがスコープの中に入っていると判断されるので
	#全ての位置で参照可能
	#
	my $file_name = $ARGV[0];		#コマンドライン引数が$file_nameに代入される

	#chompで末尾の改行文字を削除する
	chomp($file_name);
	
	#元のファイルは大抵Shift-JISのため、Windowsの文字コード(cp932)を指定する
	use open IN  => ":encoding(cp932)";
	
	#コマンドライン引数で指定したファイルを読込モードで開く
	open (FILE, "<$file_name") or die "ファイルがありません\n";
	
	#ファイルを1行づつ処理する
	while (my $line = <FILE>){
		
		#改行を削除する
		chomp($line);
		
		#カンマで行を分けて配列に格納
		#生ログの情報は
		#経度,緯度,受信レベル,アンテナ本数,位置精度,SNR,年月日,時分秒msec,取得元,Mark
		my ($day, $location, $phone, $nw_type, $download, $upload )= split(/,/, $line, 6);
		#print Encode::encode('shiftjis',$location."\n");
		
		#付近という文字以降を削除
		$location =~ s/ 付近.*$//;
		
		# 数字は半角にそろえておく
    	    $location =~ y/1234567890/1234567890/;
		# 単独の0は「ゼロ」に書き換える
		$location =~ s/(?<!\d)0(?!\d)/ゼロ/g; 
		# 桁区切りのコンマを取っておく
		$location =~ s/(?<=\d),(?=(\d\d\d)+(?!\d))//g;		
		# 4桁ごとに区切って万億兆を挿入
		$location =~ s/(?<=\d)(?=\d\d\d\d(?!\d))/万/g;
		$location =~ s/(?<=\d)(?=\d\d\d\d(?=万))/億/g;
		$location =~ s/(?<=\d)(?=\d\d\d\d(?=億))/兆/g;
		# 下から1桁ずつ十百千を挿入
		$location =~ s/(?<=\d)(?=\d(?!\d))/十/g;
		$location =~ s/(?<=\d)(?=\d(?!\d))/百/g;
		$location =~ s/(?<=\d)(?=\d(?!\d))/千/g;
		# 0になってる桁を削除
		$location =~ s/0[十百千]//g;
		$location =~ s/億0万/億/g;
		$location =~ s/兆0億/兆/g;
		$location =~ s/0//g;
		# 十百には1をつけない
		$location =~ s/1([十百])/$1/g;
		# 各桁を漢数字に書き換え
		$location =~ y/123456789/一二三四五六七八九/;

		
		
		print Encode::encode('shiftjis',$location."\n" );
				
		#Windows7への出力はShift-JISで(こうしないと文字化けする)
		#最後の*はスペースが入っていない事を確認するための印
		#print Encode::encode('shiftjis',$location.'*' );
	}
	close FILE;