Wednesday, April 11, 2012

PerlでGPXファイルを日付ごとに分割する

Perlの初心者ですが、練習として、ある期間にわたって記録されたログデータを日ごとに分割するプログラムを作ってみました。以下の処理をPerlで実現することにもなっています。
書式指定した文字列を作成する
文字列から一番目のタグを取り出す
文字列から指定したパタンのマッチング
サブルーチン(関数)の書き方
サブルーチンの引数の代入。また、関数にファイル名や配列の渡し方
配列の使い方。FIFOとしての使い方(PUSH,POP,初期化(空にする))。配列に配列を追加する
ハッシュ(連想配列)の使い方
ファイル操作。ファイルからの入力、ファイルへの出力。ファイルの削除
ソースコードは、以下の通りです。
-------------------------------------------
#!/usr/bin/perl
use strict; #これを使うと、バグが減る!
my $file=$ARGV[0];
open (IN, $file) or die "$!";
my $tag;
my $tagStart;
my $tagEnd;
my $pattern;
my @header = (); #配列でヘッダー情報を一時保管する
my $headerCount = 0;
my $headerEnd = "<bounds.*\/>";
my $isHeader = 1;
my $minWPTCount = 20;
my $outFileCount = 0;
my $outFileName;
my $outFileNameRoot = "out";
my $currDate = "";
my $prevDate = "";
my @trkptElement = ();
my @trkptElementList = ();
my $elementType = "";
my $countElements = 0;
my $trackPointKey = "trkpt";
my $lon;
my $lat;
my $startTime = "";
my %trkPtRange = ( #ハッシュ配列を使ってトラックの点情報を記録
  "latmin" => '0.0',
  "latmax" => '0.0',
  "lonmin" => '0.0',
  "lonmax" => '0.0'
);
my $fo; #出力のファイルハンドらー
sub findKey() #文字列にあるキーワードがあるかどうかをチェックする
{
  my $ret = $_[0];
  my $key = $_[1];
  $ret =~ m/$key/;
}
sub findTag() #XMLのタグを取り出す
{
  my $ret = $_[0];
  $ret =~ /<(.*?)>/;
  $ret = $1;
}
sub getLatLon() #緯度経度を取り出す関数
{
  my $ret = $_[0];
  my $lon;
  my $lat;
  $ret =~ /"(.*?)"/;
  $lat = $1;
  $ret = $'; # ポストマッチの結果を使う
  $ret =~ /"(.*?)"/;
  $lon = $1;
  return ($lat, $lon);
}
sub printElement() #XML要素を引数のファイルハンドらーに出力する
{
  my ($fh, $array) = @_; #引数はファイルハンドらーと要素の配列
  print $fh @$array;
}
# ここからは mainに相当;入力ファイルにあるすべての行を逐次に処理
while(<IN>) {
  my $line = $_;
  if ($isHeader == 1)
  {
    $header[$headerCount] = $line;
    $headerCount++;
    if ($line =~ /$headerEnd/) {
      $isHeader = 0;
      $line = $&;
    }
  } else {
    if (&findKey($line, "^<$trackPointKey")) { #文字列のパターンマッチング
      ($lat, $lon) = &getLatLon($line);
      if ($lat > $trkPtRange{"latmax"}) {
          $trkPtRange{"latmax"} = $lat;
      } elsif ($lat < $trkPtRange{"latmin"}) {
          $trkPtRange{"latmin"} = $lat;
      }
      if ($lon > $trkPtRange{"lonmax"}) {
          $trkPtRange{"lonmax"} = $lon;
      } elsif ($lon < $trkPtRange{"lonmin"}) {
          $trkPtRange{"lonmin"} = $lon;
      }
      $trkptElement[0] = $line;
      $elementType = "$trackPointKey";
      $countElements++;
    } else {
      $tag = &findTag($line);
      if ($elementType eq "$trackPointKey") {
        if ($tag eq "ele") {
          $trkptElement[1] = $line;
        } elsif ($tag eq "time") {
          $trkptElement[2] = $line;
        } elsif ($tag eq "name") {
          $trkptElement[3] = $line;
        } elsif ($tag eq "cmt") {
          $trkptElement[4] = $line;
        } elsif ($tag eq "desc") {
          $trkptElement[5] = $line;
        } elsif ($tag eq "\/$trackPointKey") {
          $trkptElement[6] = $line;
          push (@trkptElementList,@trkptElement);
        }
      }
      if ($tag eq "time") {
        if ($countElements == 1) {
          $startTime = $line;
        }
        $tagStart = "<" . $tag . ">";
        $tagEnd = "</" . $tag . ">";
        $pattern = $tagStart . ".*" . $tagEnd;
        $line =~ /$pattern/;
        my $tmp = $&;
        $tmp =~ s/$tagStart//g; #get rid of the starting tag
        $tmp =~ s/$tagEnd//g;   #get rid of the ending tag
        my $year = substr($tmp, 0, 4);
        my $month = substr($tmp, 5, 2);
        my $day = substr($tmp, 8, 2);
        my $currDate = $year . "/" . $month . "/" . $day;
        if ($prevDate eq "" || $prevDate ne $currDate) {
          $outFileName = sprintf("%s%03d.gpx",$outFileNameRoot, $outFileCount);
          open($fo, ">$outFileName") or die "$!";
          if ($prevDate ne "") {
            #分割したファイルの日付と範囲情報をその日のものに設定
            $header[7] = $startTime;
            $header[8] = "<bounds minlat=\"" . $trkPtRange{"latmin"} . "\" minlon=\"" . $trkPtRange{"lonmin"} . "\" maxlat=\"". $trkPtRange{"latmax"} ."\" maxlon=\"". $trkPtRange{"lonmax"} ."\"\/>";
            &printElement($fo, \@header); #output the header
            &printElement($fo, \@trkptElementList);
            print $fo "</gpx>\n";
            close($fo); #ファイルを閉じる
            if ($countElements > $minWPTCount) {
              #指定点数上のログしか出力しない
              $outFileCount++;
            }
          }
          @trkptElementList = ();
          $countElements = 0;
        }
        if ($countElements == 0) {
          $trkPtRange{"latmin"} = $lat;
          $trkPtRange{"latmax"} = $lat;
          $trkPtRange{"lonmin"} = $lon;
          $trkPtRange{"lonmax"} = $lon;
        }
        $prevDate = $currDate;
      }
    }
  }
}
close($fo);
# 最後のファイルには、指定点数以下しかなければ、削除する
if($countElements< $minWPTCount) {
  unlink $outFileName;
}

No comments:

Post a Comment