てぃーだブログ › 作る人 (つくるんちゅ)日記 › CGI FLASH JavaScript › iPhoneでリアルタイムGPSロガー! Perl Script(その1)



2011年01月04日

iPhoneでリアルタイムGPSロガー! Perl Script(その1)

iPhoneでリアルタイムGPSロガー! Perl Script(その1)昨年12月の那覇マラソンでは、iPhoneから位置情報を送って、グーグルマップに走行経緯を表示する実験を、このブログ上で行ってみました。
 → GPS那覇マラソン作戦! (iPhoneからリアルタイムで更新中)


市販のGPSロガーを使っても、自宅に帰ってからPCでアップロードすることで、走行経路(軌跡)を知る事ができますが、iPhoneだとイベント中も(電波を使って)リアルタイムに位置を表示・公開できるのが面白いところ。

那覇マラソンの当日、僕がどこらへんを走っているか、ブログでずっと表示していたんです。(延べ1000人以上、見て頂いたみたい!)


オリジナルのスクリプトは以下。 Keiyaさんのサイトからお借りしました。
→ 【GPSロガー】iPhoneのGPSをJavaScriptから使って自分の居場所をログる

今回、ご紹介するのは、僕が那覇マラソン用に改造したもの。
上記のスクリプトに、距離計測機能を追加して、使用しています。(大会当日は、まだミスがあって距離が合ってません。→修正済、解説は後日。)


眠らせて置くのも勿体無いので、公開しちゃいます。(大会前に慌てて書いたので雑ですが‥)

合計、3つのスクリプトで構成。 少し長くなるので、3回に分けて解説しますね。


その1、iPhone用、位置情報送信スクリプト


iPhoneの3GS以降では、付属のブラウザー「Safari」を使い、緯度経度情報をサーバに送る事が可能です。
→ ついに iPhone Safari ブラウザから位置情報を取得できるようになります

これに、Ajax機能を付けて、ログを記録したり、軌跡付き地図を表示するようにしたのが、Keiyaさんのオリジナルのスクリプト。

で、それをまた、ちょこっとだけ改造したのが、僕の書いた物、というわけ。

注:以下の内容・表示は、オリジナルから少し変更してあります。

iPhoneでリアルタイムGPSロガー! Perl Script(その1)最初に、iPhone側の表示から。

まずは設定画面。
Interval:」は秒数指定。ここで設定した一定間隔毎に送信するよう設定してあります。

Tolerance:」の部分は、位置情報の精度の指定。

ログを見ると分かりますが、精度が良い場合は、だいたい「5」か「10」程度のデータが送られています。感覚的には「5」で誤差数メートル、「10」で10数メートル程度。「30」だと通り一本分くらいズレています。

ここの指定は、指定誤差以下のデータのみ送信する。というもので、数値を小さくしたからと言って、データの正確性が上がるとは限りません。

iPhoneでリアルタイムGPSロガー! Perl Script(その1)
▲ログの内容。

iPhoneでリアルタイムGPSロガー! Perl Script(その1)次に、通信画面。

うまく働いている場合は、緯度経度、誤差情報、送信回数、最後に送信した時間を表示しています。
ちなみに、裏側では、計測した時間も(理由は後日述べます)同時に送っています。

なお、下部の「nau.cgi」へのリンクは、ツイートする際に使います(後日紹介)。

以下、「iPhone用、位置情報送信スクリプト」の内容です。
#!/usr/bin/perl

use strict;
use warnings;
use CGI;

my $script = 'loc1.cgi';
my $logfile = 'log.txt';

my $q = new CGI;
my $int = $q->param('int');
my $int_t = $int*1000;
my $alim = $q->param('alim');
my $lati = $q->param('lat');
my $long = $q->param('long');
my $accu = $q->param('acc');
my $date = $q->param('date');

print "Content-Type: text/html; charset=UTF-8\n\n";

if (defined $int) {
&log($int,$alim);
} elsif (defined $lati and defined $long) {
&wlog($lati,$long,$accu,$logfile);
} else {
&html;
}

sub log
{
my $out=<<__EOM__;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="minimum-scale=1.0, width=device-width, maximum-scale=1.0">
<title>posupdater</title>
<script type="text/javascript" src="xmlhttp.js"></script>
<script>
var count = 0;

function locSend(lati,longi,acc) {
httpObj = createXMLHttpRequest(loc);
var date_c = new Date();
var date_c = date_c.getTime();

if (httpObj) {
httpObj.open("GET",("$script?lat="+lati+"&long="+longi+"&acc="+acc+"&date="+date_c),true);
httpObj.send(null);
}
}

function loc(){
if ((httpObj.readyState == 4) && (httpObj.status == 200)) {

var date = new Date();
var Month = date.getMonth()+1;
var Day = date.getDate();
var Hour = date.getHours();
var Minute = date.getMinutes();
var Second = date.getSeconds();
if (Hour < 10) Hour="0"+Hour;
if (Minute < 10) Minute="0"+Minute;
if (Second < 10) Second ="0"+Second;

\$("res").innerHTML = "<b>Loc Sent.</b> ( "+count+" )<br/>lastup : "+ Month + "/"+Day+" "+Hour+":"+Minute+" "+Second;
}else{
\$("res").innerHTML = "<b>Sending...</b>( "+count+" )";
}
}
function locupdate(pos) {

var d = document.getElementById("d");
d.innerHTML = "lat : " + pos.coords.latitude + "<br/>long : " + pos.coords.longitude + "<br/>accuracy : " + pos.coords.accuracy + "<br/><br/>";

if (pos.coords.accuracy < $_[1]) {
locSend(pos.coords.latitude,pos.coords.longitude,pos.coords.accuracy);
count ++;
}else{
\$("res").innerHTML = "<b>Can't Sent.</b>";
}
}


function handleError(a) {
var d = document.getElementById("d");
d.innerHTML = "<p> error: " + a.code + "</p>";

}

setInterval("navigator.geolocation.getCurrentPosition(locupdate, handleError)",$int_t);

</script>
</head>
<body>
<div>
<p>Your location</p>
<div id="d"></div>
<div id="res"></div>
</div>
<p><a href="nau.cgi" target="_blank">nau.cgi</a></p>
</body>
</html>
__EOM__

print $out;
}

sub wlog
{
my $latitude = $_[0];
my $longitude = $_[1];
my $accuracy = $_[2];
$latitude =~ s/[^\d\.]+//;
$longitude =~ s/[^\d\.]+//;
$accuracy =~ s/[^\d\.]+//;

open( NEW, ">> $_[3]" );
flock(NEW,2);
print NEW "$latitude,$longitude,$accuracy,$date,\n";
close( NEW );
}

sub html
{
print <<__EOM__;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>posupdater</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="minimum-scale=1.0, width=device-width, maximum-scale=1.0">
</head>
<body >
<h1>posupdater Setup</h1>
<form name="setup" action="$script" method="GET">
<p>
Interval: <input type="text" name="int" value="15"><br>
Tolerance: <input type="text" name="alim" value="31">
</p>
<input type="submit" value="Done">
</form>
<hr>
<address>
Copyright © 2010 Keiya CHINEN. All rights reserved.(Edit:IGU)<br>
</address>
</body>
</html>
__EOM__

}


なにやら行数が多いですが、これは実験用のため。ランニング中、一目でちゃんと動いているか確認できるよう、時間やカウント表示部分を追加しているからです。

なお、バックグラウンドで別のGPSアプリを起動しておくと、より正確な数値が得られるようです。
僕は無料アプリ「Tweet Runners」を使用。

「Safari」はスリープ状態では通信してくれないので、画面はつけっぱなし。(設定 → 一般 → 自動ロック → しない)
GPSアプリも電気を食うので、当然、バッテリーの持ちは悪くなります。

フル充電で、電波状態にもよりますが、だいたい2時間半程度といった所。
長時間使用する場合は乾電池式チャージャーが必要です。

その2に続く



PS:オリジナルのスクリプトは Creative-Commons Attribution, BYライセンスと伺いました。
(製作者のKeiyaさんの名前、もしくはリンクを貼ることで、コピー,再配布,改造可)

PS2:ここは専門家向けではないので、ややこしい話は端折っていますが一点だけ追記。
通常、緯度経度を定期的に取得するのは「watchPosition」を使います。今回は、裏でGPSソフトを使う前提で「getCurrentPosition」を使用。コンスタントにTolerance(公差)5か10を得られます。GPSソフトを使わないと、100~とか‥(沖縄の場合)。





同じカテゴリー(CGI FLASH JavaScript)の記事



この記事へのコメント
初めまして。

とても面白そうなことをやっていて、
自分のiPhoneでもやってみたいと思いました。

しかし、Perl等の知識はあまりなくunixのターミナル等が
使えるレベルです。。。

大変お手数なのですが、ソースをzipなどでまとめてアップしていただくことは可能でしょうか?

ファイル名等がわからず苦戦しています。。。
お忙しいとは思いますが、もし可能でしたら是非御願い致します。
Posted by tarou at 2011年01月23日 15:05
どうも、はじめまして。

ソースは、アイコンの画像とかもまとめて、次回、第3話でZIPにする予定です。
(設定方法とかも)

たぶん、一週間以内には書くと思うけど、それまで少しだけ待ってくださいね。
Posted by IGUIGU at 2011年01月23日 22:37
※このブログではブログの持ち主が承認した後、コメントが反映される設定です。
上の画像に書かれている文字を入力して下さい
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。

※記事内容に言及無いコメントは、コメントスパムと判断して削除する場合があります。