#!/usr/bin/perl =pod NMEA-0183ログデータと EXIF付き JPEG画像ファイルから Google Maps APIを用いた画像付きルートマップHTMLを作成する 使い方: あらかじめカレントディレクトリに サムネイル画像 XXXXXs.jpg と拡大画像 XXXXXl.jpg を作成しておく。 その後、NMEA-0183ログデータファイルを引数にして実行する。 ログデータファイルは複数指定可。ただし、時系列に並んでいること。 標準出力に HTMLが出力されるので適当なファイル名でリダイレクトすること。 他の人が使う場合は Google Maps APIキーを取得して書き変えること。 タイトルや画像の名称、コメントなどは HTML作成後、手作業で書き変える。 例: $ gpsroute 20100420.LOG 20200421.LOG > travel.html =cut use strict; #use warnings; use Time::Piece qw/localtime gmtime/; use Image::ExifTool; my $html_title = "%TITLE%"; my $google_api_key = "ABQIAAAAqJbV3H_KsOOCocfG42JrwhS-ttq4eYygrsJtW1dP-IBsl4Q2dxTODqZie8XdfLgYv0ra3LKYMrp8rw"; my @lat; my @long; my @alt; my @time; my @speed; my $lat_min = 90; my $lat_max = -90; my $long_min = 180; my $long_max = -180; my $data_n = 0; #my @image_file; #my @image_time; #my @image_lat; #my @image_long; #my @image_alt; #my @image_title; #my @image_width; #my @image_height; my @image; my $image_n = 0; # カレントディレクトリの画像ファイルのタイムスタンプ取得 opendir(DIR, ".") or die "Cannnot open current directory\n"; my @dir = readdir(DIR); closedir(DIR); foreach my $imagefile (@dir) { if ($imagefile =~ /[^s]s\.(JPG|jpg)$/){ my $info = Image::ExifTool->new->ImageInfo($imagefile); my $time = $$info{'CreateDate'}; my $datetime = Time::Piece->strptime($time, "%Y:%m:%d %H:%M:%S"); my @size = split(/x/, $$info{'ImageSize'}); $image[$image_n++] = { 'file' => $imagefile, 'time' => $datetime, 'width' => $size[0], 'height' => $size[1] } } } # タイムスタンプでソート @image = sort sort_by_time @image; my $img = 0; my $param = 0; # ルート情報取得 foreach my $logfile (@ARGV){ my $lat; my $long; my $alt; my $time; my $datetime; my $speed; my $status; my $dir; my $old_lat; my $old_long; my $old_dir; my $old_datetime; if ($logfile =~ /\-(\w+)/){ if ($1 eq 'raw'){ $param = 'raw'; } next; } open(my $fh, '<', $logfile) or die "Cannot open $logfile : $!"; while (my $line = <$fh>){ if ($line =~ /^\$GPGGA/){ my @data = split(/,/, $line); $alt = $data[9]; } elsif ($line =~ /^\$GPRMC/){ my @data = split(/,/, $line); $data[1] = substr($data[1], 0, index($data[1], ".")); $datetime = Time::Piece->strptime("$data[9] $data[1]", "%d%m%y %H%M%S"); $datetime += 9 * 3600; # +09:00 $time = $datetime->ymd . " " . $datetime->hms; $status = $data[2]; $lat = $data[3]; $long = $data[5]; if ($data[4] eq 'S'){ $lat *= -1; } if ($data[6] eq 'W'){ $long *= -1; } $lat = (int($lat/100)) + (($lat/100) - int($lat/100)) / 60 * 100; $long = (int($long/100)) + (($long/100) - int($long/100)) / 60 * 100; $lat = int($lat * 1000000 + 0.5) / 1000000; $long = int($long * 1000000 + 0.5) / 1000000; } elsif ($line =~ /^\$GPVTG/){ my @data = split(/,/, $line); $speed = $data[7]; } if (($alt ne undef)&&($time)&&($speed ne undef)){ # 1件分のデータがそろった if (($old_lat != $lat)&& ($old_long != $long)){ if ($lat > $lat_max){ $lat_max = $lat; } if ($lat < $lat_min){ $lat_min = $lat; } if ($long > $long_max){ $long_max = $long; } if ($long < $long_min){ $long_min = $long; } # 画像ファイルのタイムスタンプと比較 while (($img < $image_n)&&($datetime >= $image[$img]{'time'})){ $image[$img]{'lat'} = $lat; $image[$img]{'long'} = $long; $image[$img]{'alt'} = $alt; # 仮のタイトル $image[$img]{'title'} = sprintf("画像 %d", $img+1); $img++; } # 前のポイントとの距離 my $dist = &get_distance($old_lat, $old_long, $lat, $long); #print "$lat $long $dist(m) $speed(km/h)\n"; if ((($speed < 0.5)&&($dist >= 1))|| # 0.5km/h以下 1m (($speed < 5)&&($dist >= 10))|| # 5km/h以下 10m (($speed < 30)&&($dist >= 200))|| # 30km/h以下 200m (($speed < 60)&&($dist >= 500))|| # 60km/h以下 500m ($dist >= 5000)|| # 高速道 5km ($param eq 'raw')){ push(@lat, $lat); push(@long, $long); push(@alt, $alt); push(@time, $time); push(@speed, $speed); $old_lat = $lat; $old_long = $long; $data_n++; } } undef $alt; undef $time; undef $speed; } } close($fh); } my $markers; my $images; for (my $i = 0; $i < $image_n; $i++){ my $tm = $image[$i]{'time'}->strftime("%Y/%m/%d %H:%M:%S"); $markers .= "\t\t\t\tmarkers[$i] = createMarker($image[$i]{'lat'}, $image[$i]{'long'}, $image[$i]{'alt'}, '$tm',\n\t\t\t\t'$image[$i]{'file'}', '$image[$i]{'title'}', '', $image[$i]{'width'}, $image[$i]{'height'});\n"; $markers .= "\t\t\t\tmap.addOverlay(markers[$i]);\n"; $images .= "\t$image[$i]{'title'}
\n"; } my $points; my $infos; for (my $i = 0; $i < $data_n; $i++){ $points .= "\t\t\t\tpoints[$i] = new GLatLng($lat[$i], $long[$i]);\n"; $infos .= "\t\t\t\tinfos[$i] = new PInfo(\"$time[$i]\", $alt[$i], $speed[$i]);\n"; } # 日本国内限定 my $lat = ($lat_max + $lat_min) / 2; my $long = ($long_max + $long_min) / 2; # HTMLを標準出力 print < $html_title 【$html_title】 ZOOM:
$images
EOM exit(0); # 距離(m)を求める。日本国内など、狭い範囲にのみ有効 sub get_distance { my ($lat1, $long1, $lat2, $long2) = @_; my $dx = 6378137 * (($long2 - $long1) / 180 * 3.14159) * cos($lat1 / 180 * 3.14159); my $dy = 6378137 * (($lat2 - $lat1) / 180 * 3.14159); return sqrt($dx * $dx + $dy * $dy); } # 移動方向(°)を求める sub get_direction { my ($lat1, $long1, $lat2, $long2) = @_; my $dx = 6378137 * (($long2 - $long1) / 180 * 3.14159) * cos($lat1 / 180 * 3.14159); my $dy = 6378137 * (($lat2 - $lat1) / 180 * 3.14159); if ($dx == 0){ if ($dy > 0){ return 0; } else { return 180; } } my $dir = atan2($dx, $dy) / 3.14159 * 180; return $dir; } # 画像データハッシュ配列のソート関数 sub sort_by_time { return $$a{'time'} <=> $$b{'time'}; }