開発日記/2013年の日記
2013/9/19
Adobe AIRのアプリを開発中に詰まったメモ。AIRでは簡易データベースとしてSQLiteが組み込まれているのでアプリの履歴に関するデータをSQLiteのテーブルに放り込んでいました。履歴データは当日分の履歴を確認できるようになっていたのですが、ある日のこと突然、履歴が表示されなくなりました。原因が確認できないまま、その日の作業は終了しましたが、翌日この問題は解消していました。再発もなかったのでOSがデータベースのファイルをロックでもしちゃったんだろうぐらいに考えていました。
数日後(今日)問題が再発したので、改めて問題を問題として再認識しました。データベースファイルは確認したところReadもWriteも問題なくできているようなので、SQL文絡みの問題と考えてデータベースの構造を疑ってみました。構造は問題なかったので、履歴データを一つずつ点検していくと、「今日」の記録の一部が、「昨日」の記録として残っていることを発見しました。時間にして9時間分のデータに問題があったので、SQLiteのデータベースのタイムゾーンの問題だと考えられます。
調べてみるとAIRのSQLiteはOSのタイムゾーン設定を無視してUTC(世界時)を採用するとかなんとかで、CURRENT_TIMESTAMPで履歴の時間を記録していると日英間の9時間の時差が含まれてしまうのでした。生データでは日付のカラムに「2456310.95046296」のような小数形式で記録されていたのでなかなか気づけませんでした。
SELECT DATETIME(column_zikan) FROM rireki
こんな風に時間を取得しているところに
SELECT DATETIME(column_zikan,'localtime') FROM rireki
'localtime'を指定してあげたら問題が解決しました。
2013/8/19
Adobe AIRのアプリを作成中に詰まったメモ。アプリのリソースに大量に音声データを追加したところ、ビルド時に「Error: Java heap space」とエラーメッセージが出るようになりました。ちなみにFlashDevelopで制作していました。
原因はコンパイル時のメモリ不足とのことで、SDKの設定ファイル上でコンパイル時に利用するメモリの量を増やす指定をすれば解決するといろんなサイトに書いてありました。
編集対象は「{SDKのインストールパス}\bin\jvm.config」で、
java.args=-Xmx384m -Dsun.io.useCanonCaches=false
初期値が↑こんな感じのところを
java.args=-Xmx1024m -Dsun.io.useCanonCaches=false
と必要な量に増やしてあげればいいとのこと。が、設定を変更しても依然として「Error: Java heap space」が表示されます。FlashDevelopを再起動しても、OSを再起動してもダメでした。
ここでふと{SDKのインストールパス}が「C:\Program Files(x86)」の中であることにピンときました。開発環境のOSがWindows 7であったため、「Program Files」フォルダはUACで仮想化されています。つまり「jvm.config」を編集したつもりでいたけど、仮想化された仮のファイルが編集されていたに過ぎず、見た目上は編集されていたようにみえても実際の「Program Files」フォルダの中身は依然として少ないメモリでコンパイルするように設定されていたのでした。
というわけでテキストエディタを管理者権限で起動して、「jvm.config」を再度編集したらバッチリ、ビルドが通りました。UAC・・・おそろしい子・・・。
2013/8/2
先日注文したLeap Motionが朝方届きました。7/26の午後4時ぐらいに注文したのでおよそ一週間ぐらいで届いたことになります。私のtwitterのタイムライン上ではもう騒動が沈静化していますが、私自身はまだこれを使った開発を楽しみにしております。商品の到着までの間に開発環境を整えてあるので、本業の合間を縫って何か作ろうと思います。
とりあえず、Disneyの「Sugar Rush」のアプリを購入して遊んでみたところ、長時間遊ぶには腕の筋肉が必要だということがわかりました。指をポインターに見立てて、項目を一定時間ホールドすることで選択するというインターフェースもプルプルして疲れます。自分で何か作る場合は、疲れないやつにしようと心に誓いました。
Windowsのウィンドウ管理がマウスだと大変なので、Leap Motionになんとかしてもらおうというのが最初の目標となりそうです。
2013/2/17
とあるプロジェクトで「投稿された写真のEXIF情報から撮影場所を特定して、地図に並べたい」という相談を受けました。表示に関してはGoogle Maps APIで写真つきのマーカーを立てるだけなので特別面白いことはなかったのですが、「写真のEXIF情報から撮影場所を取得する」という部分に興味が湧きました。
昨年(2012年)末に遠隔操作ウィルス事件の犯人から送られてきた画像にEXIF情報が含まれており、そこから撮影場所を特定する際に「千葉県鎌ヶ谷市」と「神奈川県横浜市」の二か所にスポットが当たりました。当時の私は「緯度と経度のペアで表せるのは地球上の一点だけなはずでは・・・」とか思いながらニュースを読んでいました。EXIF情報の仕様について調べたことはなかったので、「ややこしいのかもな(知らんけど)」と思ったものです。
仕事でEXIF情報に触れるときは、多くの言語で専用のライブラリが用意されているので、必要なデータを取得するのもライブラリ任せで問題ないのですが、今回のWEBシステムはperlの開発案件で、一般的に使われるImage::ExifToolのライブラリがレンタルサーバーに標準では備わっていなかったこともあって、それであれば必要な部分だけ取りだす処理を書いてしまおうということになりました。
JPEGファイルの仕様については何度か調べたことがあるので、ファイル内を走査してマーカー探して・・・みたいな段取りをまず書きました。参考にしたサイトはこちら 簡単に説明すると
- EXIF情報が入っているかを調べる
- ファイル内のエンディアンを調べる
- GPS関連情報の場所を探す(Tag:34853)
- 撮影場所の緯度、経度に関する情報を取得する
という流れでした。撮影場所に関する情報は
名前 | タグ番号 | データ構成 | 内容 |
GPSLatitudeRef | 1 | ASCII(2) | 北緯なら'N'、南緯なら'S' |
GPSLatitude | 2 | RATIONAL(3) | 緯度(それぞれ度分秒) |
GPSLongitudeRef | 3 | ASCII(2) | 東経なら'E'、西経なら'W' |
GPSLongitude | 4 | RATIONAL(3) | 経度(それぞれ度分秒) |
こんな構成です。ASCII値は文字列の終端のNULLも含むのでイニシャルと合わせて2バイトになるそうです。RATIONALは日本語でいうところの有理数のことです。有理数は分子と分母の二つの整数で表せる数のことですので、RATIONALはLONG値(4バイト)が2つで8バイトの情報です。RATIONAL3つで経緯度の度分秒をそれぞれ表しますので4バイトずつ6つの整数値をD1,D2,M1,M2,S1,S2と名前をつけると
D1/D2 度 M1/M2 分 S1/S2 秒
と表せます。度分秒という単位は60進法表現なので、1度=60分、1分=60秒となります。これを百分率表現(1度=100分、1分=100秒)と勘違いすると鎌ヶ谷市が横浜市になるんだそうです。Google Maps APIで利用される経度と緯度は度分秒ではなく、小数表現を含む度で表されますので、
D1/D2 + M1/M2/60 + S1/S2/3600 度
と計算します。また日本の座標位置だとあまり意識されないかもしれませんが、Google Mapsでは、西経や南緯に負の数を割り当てているので、「GPSLatitudeRef」が'S'だったり、「GPSLongitudeRef」が'W'の場合は-1をかけてあげる必要があります。
さて、これで大丈夫と思いきや、もう一つ問題が残されていました。それは度分秒の表現方法が一様ではないという点です。度分秒の3値を全て使うDMS方式の他にも、秒は使わずにMを小数表現にするDM方式(DMM方式)、分秒を使わず度の小数表現で表すD方式(DDD方式)なんてものもあるそうです。これはGPS端末やプログラムによって表現しやすい方式を採用しているのでしょう。従って、同じ場所に立って携帯電話のカメラで写真を撮影した場合でも、端末やアプリの種類によってJPEGに埋め込まれる経緯度の記録方式が変わることがあります。例えば30度30分36秒を表現する場合、
D1 | D2 | M1 | M2 | S1 | S2 | |
CASE 1 | 30 | 1 | 30 | 1 | 3600 | 100 |
CASE 2 | 30 | 1 | 30 | 1 | 36 | 1 |
CASE 3 | 30 | 1 | 3060 | 100 | 0 | 1 |
CASE 4 | 3051 | 100 | 0 | 1 | 0 | 1 |
といったように多くの種類の表現方式が存在しえます。ここで注目してほしいのがCASE 3の「秒」とCASE 4の「分秒」の部分です。両者は使われていないので「0/1」というRATIONAL表現になっています。多分、仕様的にもそうすべきと書いてある・・・のかな。しかし、世の中にはそれに従っていないケースもあるらしく、検索して調べていたら、使われないM2やS2が「0」で埋められているケースがなくもないらしいです、昔の携帯電話とかにあったとかなんとか。これをそのまま計算するとM1/M2=0/0、S1/S2=0/0なので計算機がゼロ除算のエラーを起こします。念のためにM2とS2が0じゃないかチェックした方がよいかもしれません。
my $ret = $d1/$d2;
if ($m2 != 0) {$ret += $m1/$m2/60;}
if ($s2 != 0) {$ret += $s1/$s2/3600;}
return $ret;
↑こんなコードを書いて、ネット上で位置情報が含まれる画像を探してきて、経緯度を取得してGoogleマップのストリートビューで検証してみたら大丈夫そうでした。