先日,一人で勝手に騒いでいたiPhone/iPodTouch向けアプリ,uTrackMe ですが,あまりにも騒ぎすぎた結果可読性を損なっているので,書き直し&アップデートします.
uTrackMe はHTTP POST リクエストで任意の Web サーバに対して uTrackMe アプリを起動してから現在までのトラックをまとめて POST する DIY チックなアプリです.GPSed と違うところは,
- WiFiや3Gが切れても,かまわず HTTP POST を繰り返す,
- 任意のサーバに対して POST できる,
- POSTできるトラック情報が多い(フォーマット参照).過去のポイントどころか,速度や向きも出力可能,
- サーバスクリプトが規定フォーマット(plist あるいは XML)にて,他ユーザのトラックログを返すときは,当該他ユーザの位置情報もアプリ上に表示できる.
ただし,
- サーバスクリプトは自分で書け.
という非常に男らしいアプリです.
というわけで,サーバスクリプトを ruby で書きました−.ぼくの ruby は ruby じゃないそうなので,とても汚いソースだ.有志の方々は是非作り直してぼくにおめぐみください.
下記のスクリプトをcgiとして動作するようにWebサーバに設置し,当該cgiファイルに chmod 777 な data ディレクトリを作成してください.あとは,当該 cgi に uTrackMe から POST するだけ.
#!/usr/bin/ruby print "Content-type: text/html\n\n" require "cgi" require "csv" require "digest/md5" cgi = CGI.new def check_format(cgi, n) param_list = [ "id", "label", "delta", "lock", "time[#{n}]", "lat[#{n}]", "lon[#{n}]", "acc[#{n}]", "spd[#{n}]", "dir[#{n}]", "alt[#{n}]" ] param_list.each do |p| return false if cgi[p].empty? end return false if cgi["time[#{n}]"].to_i < 1274713660 return true end def get_str(cgi, n) param_list = [ "label", "delta", "lock", "time[#{n}]", "lat[#{n}]", "lon[#{n}]", "acc[#{n}]", "spd[#{n}]", "dir[#{n}]", "alt[#{n}]" ] str = Time.at(cgi["time[#{n}]"].to_i).strftime("%Y/%m/%d-%H:%M:%S").to_s + "," + Digest::MD5.hexdigest(cgi["id"]) + "," + param_list.map{|p| cgi[p]}.join(",") return str end if cgi["label"] != "" then i = 0 while i < 86400 if cgi["time[#{i}]"] == "" n = i break end i += 1 end n -= 1 t = cgi["time[#{n}]"].to_i time = Time.at(t) if check_format(cgi, n) != true print "[Err] Got string now was invalid.\n" exit end str = get_str(cgi, n) if File.exist?("./data/" + Time.at(t).strftime("%Y%m%d").to_s) == true fn = "./data/" + Time.at(t).strftime("%Y%m%d").to_s else if File.exist?("./data/" + Time.at(t-1).strftime("%Y%m%d").to_s) == true fn = "./data/" + Time.at(t-1).strftime("%Y%m%d").to_s end fn = nil end if fn != nil f = open(fn, "r") last = f.gets last = f.gets if last != nil f.seek(-2, IO::SEEK_END) until (c = f.getc) == ?\n || c == ?\r f.seek(-2, IO::SEEK_CUR) end last = f.gets else f.rewind last = f.gets end f.close last_t = CSV.parse(last)[0][5] else last_t = "0" end i = 0 forgotten_num = 0 last_put_time = 0 while i < 86400 && cgi["time[#{i}]"].to_i < t if cgi["time[#{i}]"].to_i > last_t.to_i + cgi["delta"].to_i forgotten_str = get_str(cgi, i) if cgi["time[#{i}]"].to_i - last_put_time > cgi["delta"].to_i File.open("./data/" + Time.at(cgi["time[#{i}]"].to_i).strftime("%Y%m%d").to_s, "a"){|f| f.write forgotten_str + "\n" } last_put_time = cgi["time[#{i}]"].to_i forgotten_num += 1 end end i += 1 end File.open("./data/" + time.strftime("%Y%m%d").to_s, "a"){|f| f.write str + "\n" } else str = "Null" end if forgotten_num > 0 print forgotten_num.to_s + " point(s) not completed just have been uploaded!" File.open("./data/error", "a"){|f| f.write Time.now.to_s + " " + "[" + cgi["label"] + "]" + forgotten_num.to_s + " point(s) not completed just have been uploaded!" + "\n" } else print "[Succ] " + str end
サーバ側に残るログは下記のフォーマットとなります.
md5(id), time, label, delta, lock, unix-epoch-time, lat, lon, acc, spd, dir, alt
基本的に,uTrackMeからのPOSTフォーマットを踏襲していますが,下記の修正を加えております.
- md5(id): idは端末固有IDなので,個人情報保護の観点から念のためMD5ハッシュしています.SHA1は若干重いので,MD5で妥協.
- time: uTrackMeのtimeはUNIX-Epochですが,ここではYYYY/MM/DD-hh:mm:ssで出力しています.
- unix-epoch-time: uTrackMeのtimeを出力.
あと,捕捉ですが,lock(location lock) は,GPS測位できていれば1,失敗しているときは0を渡すようです.ただし,uTrackMe が foreground 動作しているときに限る.
まー,いろいろとまだバグが潜んでいますが,まずは,こいつで遊んでみたいと思います.
ちなみに,uTrackMe はたまに嘘情報を POST してくる.時刻が 0 になっていたり,パラメタがからっぽだったりすることも.気をつけよう.