D3.js v4

v4の情報

D3.js v4の情報ってまだあまりないんですね。見つけられたのはこれくらい。

v3で学習→変更点を理解

v3ならチュートリアルやサンプルが山ほどあるので、それで勉強してv4への変更点を確認するのも手かなあ。

おまけ

D3.jsを使って地図を作成(Windows版)

久しぶりにコードを書きたくなり、普段仕事がインフラ系なのでなんかビジュアル系のものがやりたくてD3.jsで地図を描くことにしました。Webのチュートリアルが古かったり、mac前提だったりして環境設定でいろいろはまったのでメモ。2017年2月5日時点でのWindows 10でのメモです。

エディタ+実行環境

Visual Studio Community 2015 にしました。JavaScriptでもインテリセンスが強力だし、F5を押したらIIS Expressを起動してくれるし。

「新規作成」>「Webサイト」>「ASP.NET空のWebサイト」でプロジェクト作成して、「追加」>「新しい項目の追加」>「HTMLページ」

その他ツール類の準備

各ツールの利用目的などの詳細は後で。

QGIS Welcome to the QGIS project!

GISデータとして一般的なSHAPEファイルを表示するツール。GeoJSONとして保存もできますがそれは次のツールを利用。

GDALのWindowsビルド GISInternals Support Site

"Stable Releases" からアーキテクチャにあったものを選んで、一番上の全部まとめたzipをダウンロード。
使うのは、ogr2ogrコマンドでSHAPEファイルをGeoJSONに変換する機能だけ。

Node.jsの環境整備 Windows の Node.js 開発環境構築 最小手順 - Qiita

このサイトの通りにやりました。次のtopojsonのために必要です。

topojson

GoeJSONをTopoJSONに変換するツール。最新版(2.x系)のtopojsonにはtopojsonというコマンドは含まれず、同じ機能はgeo2topoコマンドになっている。geo2topoで作成したTopoJSONファイルは、Webで情報が豊富な"//d3js.org/topojson.v0.min.js"で読み込むとエラーになるようなので注意。なので、あえて古い1.6.27をインストール。

> npm install -g topojson@1.6.27

gオプションを忘れるとカレントディレクトリのプロジェクトにインストールしようとするので注意。

データの入手

国土数値情報(国土交通省) 国土数値情報ダウンロードサービス

日本のいろいろなデータがあります。ただ、簡単な日本地図を描きたいのに適したデータは見つからず。「行政区域」データが日本地図を描くのに使えそうですが、データがかなり大きいです(日本全国で235MB)。

どういうデータが提供されているかは、国土情報ウェブマッピングシステムで確認できます。

Natural Earth Downloads | Natural Earth

世界地図と様々なデータがあります。Scaleは一番細かい"Large scale data, 1:10m"でも上記の日本だけの行政区域よりサイズが小さいです。"Admin 1 – States, Provinces" > "Download states and provinces"をダウンロード(13.9MB)。

データの加工

SHAPE -> GeoJSON

入手したSHAPEファイルを、D3.jsで扱えるGeoJSONに変換します。日本のみにフィルタも。

> ogr2ogr -f GeoJSON -where "adm0_a3 = 'JPN'" ne_pref_japan_geo.json ne_10m_admin_1_states_provinces.shp
GeoJSON -> TopoJSON

GeoJSONを、効率化してサイズを小さくしたり機能追加したTopoJSONに変換。

> topojson -o ne_pref_japan_topo.json ne_pref_japan_geo.json

コーディング

GeoJSONの表示
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <title></title>
    <style>
    </style>
</head>
<body>
    <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script>
        var width = 800;
        var height = 800;

        //地図を描く先のsvg要素を準備
        var svg = d3.select("body")
            .append("svg")
            .attr("width", width)
            .attr("height", height);

        //JSONファイルを読み込み、コールバックで描画処理
        d3.json("ne_pref_japan_geo.json", function (error, data) {
            console.log(data);

            //緯度経度→画面座標の投影方法を指定。
            var projection = d3.geo.mercator()
                .center(d3.geo.centroid(data))
                .scale(1200)
                .translate([width / 2, height / 2]);

            //上記投影方法をオプションとして投影を行うfunctionを生成。
            //ここで生成したfunctionであるpathは、GeoJSONのデータがinput、
            //svgのpath要素のd属性に指定するパスコマンドがoutput。
            var path = d3.geo.path()
                .projection(projection);

            //描画処理本体。
            //D3.jsの機能を使いGeoJSONのデータ個数に合わせてpath要素を生成、
            //path要素に対してGeoJSONのデータを紐付ける。(appendまで)
            //最後のattrが肝。第二引数のpathは、上で作成したfunction。
            //path functionは、path要素に紐付けられたGeoJSONデータを入力とし、
            //projectionを踏まえて画面座標に投影したsvgのパスコマンドを出力し、
            //それがd属性にセットされる。
            svg.selectAll("path")
                .data(data.features)
                .enter()
                .append("path")
                .attr("d", path);
        });

    </script>
</body>
</html>


sample

TopoJSONの表示
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <title></title>
    <style>
    </style>
</head>
<body>
    <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script src="//d3js.org/topojson.v0.min.js" charset="utf-8"></script>
    <script>
        var width = 800;
        var height = 800;

        var svg = d3.select("body")
            .append("svg")
            .attr("width", width)
            .attr("height", height);

        d3.json("ne_pref_japan_topo.json", function (error, topo) {
            console.log(topo);

            //読み込んだTopoJSONをGeoJSONに変換
            //objectsの次の要素(ne_pref_japan_geo)は、変換元のGeoJSONのファイル名。
            var data = topojson.object(topo, topo.objects.ne_pref_japan_geo);

            var projection = d3.geo.mercator()
                .center(d3.geo.centroid(data))
                .scale(1200)
                .translate([width / 2, height / 2]);

            var path = d3.geo.path()
                .projection(projection);

            svg.selectAll("path")
                .data(data.geometries)  //GeoJSONの時と変わるので注意。
                .enter()
                .append("path")
                .attr("d", path);
        });

    </script>
</body>
</html>

sample

コードの理解のために

D3.jsの仕様とSVGの仕様は理解する必要があります。これらのサイトでまず概要理解しました。

次やること

使っているライブラリのバージョンが低いので、まずは最新に追従しようと思います。
D3 API Reference
その後、地図にいろいろ書き込んだり、インタラクティブにしたり。

AR系アプリ作成に必要な要素 その4

前回の続き。

  1. カメラプレビューへのオーバレイ表示
  2. 端末の向いている方角/姿勢の取得
  3. 端末の現在地(緯度/経度)の取得
  4. カメラの画角情報取得
  5. 2-4の情報から表示情報を計算

とりあえず上記の1-5までが実装出来ましたが、加速度センサがノイズだらけでターゲットが激しく動き回ってしまいました。ノイズの除去が必要なようです。

加速度センサ/地磁気センサのノイズ除去

まず、移動平均を取ってみましたが、スパイクノイズに引っ張られてしまいイマイチ。
そこで、スパイクノイズに強いメディアンフィルタをかけてみました。過去3サンプルと5サンプルで比較して安定した5サンプルを採用。
それでも、小刻みな振動が気になったので、メディアンフィルタの出力に対して、ローパスフィルタをかけました。
これでそれなりに安定しました。

磁場の補正

加速度センサのノイズが安定したところで、対象物を正しく補足できているか確認したところ、常に数度のずれが発生。
その1で書いた、磁気偏角の補正はやはり必要なようです。GeomagneticFieldクラスを使います。
http://developer.android.com/reference/android/hardware/GeomagneticField.html

地磁気センサの出力は、getRotationMatrix()に食わせて回転行列にしてしまったので、world's coordinate system のZ軸周りの回転には不向き。
そこで、対象物をworld's coordinate system で表した座標系のほうをZ軸周りに回転させて補正することにしました。

これで、磁気偏角の補正も完了し、対象物を補足できるようになりました。

しかし、それは屋外の条件のみ。鉄筋の建物の内部では、地磁気が乱れるため正しい方角が得られないようです。これは原理的にどうしようもなさそう。

https://groups.google.com/forum/#!topic/android-group-japan/69LPDTJTWzA
「屋外のような外乱影響の少ないところでは、地磁気方向(北)を正確に測定できると思いますが、ビルなどの屋内では鉄筋の影響を受けて磁場が歪んでいるので、正確な地磁気方向を測定することは難しいと思います。」

AR系アプリ作成に必要な要素 その3

1-4はデータを取れたので、いよいよ5に入ります。

  1. カメラプレビューへのオーバレイ表示
  2. 端末の向いている方角/姿勢の取得
  3. 端末の現在地(緯度/経度)の取得
  4. カメラの画角情報取得
  5. 2-4の情報から表示情報を計算

注意:以下はメモ書きです。アプリはまだ完成していないので正しいかわかりません。

方針

最終的には、対象物をカメラ画像にオーバレイしてディスプレイに投影するので、対象物の位置をデバイス座標系(device coordinate system)で表現する必要があります。

これまでに得られている、対象物の位置、デバイスの位置は、(緯度, 経度, 高さ)の極座標なので、座標変換をやりやすくするために、地球の中心を原点とした三次元直交座標系に変換する必要がありそう。
また、AndroidのセンサーAPIで得られる端末の回転状態は、(地球に対して北向きの接線, 東向きの接線, 地表から空に向かう法線)による座標系(world's coordinate system)なので、この変換も必要。
http://developer.android.com/reference/android/hardware/SensorManager.html
getRotationMatrix()を参照。

まとめると、

  1. 対象物の位置、端末の位置を、極座標(緯度, 経度, 高さ)から三次元直交座標系に変換
  2. 対象物の位置、端末の位置を、センサーAPIのworld's coordinate system(端末が原点)で表現
  3. 端末の向き(=カメラの向き)の座標系(device coordinate system)に回転
  4. ディスプレイに投影

という流れになります。

極座標(緯度, 経度, 高さ)から三次元直交座標系に変換

地球が完全な球ではない(GPS極座標系も地球を回転楕円体と定義)ため単純な計算ではなさそうで、いきなり難易度が高い。。。

このサイトの図でイメージをつかみ、
http://vldb.gsi.go.jp/sokuchi/datum/tokyodatum.html
このサイトの計算式を使って進めています。
http://homepage1.nifty.com/aida/jr1huo_calsat32/Calsat32Keiido.htm

2点間の距離はそれなりの精度で取得できたので、大きな間違いはなさそう。

センサーAPIのworld's coordinate systemで表現

上記サイトの図の直交座標系から、getRotationMatrix()の座標系への変換です。
3次元座標変換で躓いたので、下記サイトを読んで勉強中。。。
http://www.mech.tohoku-gakuin.ac.jp/rde/contents/course/robotics/coordtrans.html

device coordinate systemに回転

これも3次元の座標変換です。getRotationMatrix()で取得した回転行列を使えるはず。デバイス座標系の定義は下記にあります。
http://developer.android.com/reference/android/hardware/SensorEvent.html

ディスプレイに投影

ここにいい感じの説明があったので参考にします。
http://opencv.jp/opencv-2.1/cpp/camera_calibration_and_3d_reconstruction.html



座標変換で頭が捻じれそうです。。。

AR系アプリ作成に必要な要素 その2

昨日は、1と2を確認したので、今日は3,4です。

  1. カメラプレビューへのオーバレイ表示
  2. 端末の向いている方角/姿勢の取得
  3. 端末の現在地(緯度/経度)の取得
  4. カメラの画角情報取得
  5. 2-4の情報から表示情報を計算

端末の現在地(緯度/経度)の取得

これは、Android Developersのチュートリアルどおりに実装。
http://developer.android.com/guide/topics/location/obtaining-user-location.html

高度も取得してサンプルアプリに表示してみたら、なんかずれてる感じ。
GPSの高度は、地球の形を近似した楕円体からの高さなので、ジオイド高で補正する必要があるようです。高度を使う場合は気をつけよう。
http://kamoland.com/wiki/wiki.cgi?HT-03A%A4%CEGPS%B9%E2%C5%D9%A4%CE%B8%A1%BE%DA

カメラの画角情報取得

下記APIで取得できました。
http://developer.android.com/reference/android/hardware/Camera.Parameters.html#getHorizontalViewAngle()


ここまでで、端末から取得する必要なデータはそろったので、後は何をどう表示するかですね。

AR系アプリ作成に必要な要素 その1

何かandroidアプリを作ろうと思いつつ、ぱっとしたアイディアが出てこないので、アプリ開発の練習もかねて、いまさらながらAR系のアプリを作ろうと思います。ネタは完成するまで秘密。

AR系アプリの動作の流れとしては、下記になると思います。

  1. 端末の現在地(緯度/経度)、端末の向いている方角/姿勢を取得。
  2. 情報表示したい対象の位置(緯度/経度)、向いている方角/姿勢を取得。
  3. 1.と2.の相対的な位置関係から、画面にオーバレイ表示する位置/大きさ/角度などを決定。この際に画面上の厳密な位置にオーバレイする場合は、カメラの画角情報が必要。
  4. 画面上にカメラプレビューにオーバレイして情報表示。

確認すべき技術要素は、下記になります。(順番はサンプルを作りやすい順にシャッフル)
今日は、とりあえず1と2のサンプル作成まで。

  1. カメラプレビューへのオーバレイ表示
  2. 端末の向いている方角/姿勢の取得
  3. 端末の現在地(緯度/経度)の取得
  4. カメラの画角情報取得
  5. 2-4の情報から表示情報を計算

カメラプレビューへのオーバレイ表示

まずはカメラプレビューの表示。Anadroid Developersのサンプルを実装するだけ。
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.html

次に画像のオーバレイ表示は、こちらのコードを参考に実装。
http://ayakix-lablog.blogspot.com/2011/09/androidoverlay.html

端末の向いている方角/姿勢の取得

下記のサイトに詳しい説明あります。最終的にはAndroid Developersを確認。
http://techbooster.jpn.org/andriod/ui/443/

remapCoordinateSystem() の使い方で混乱したときに下記サイトの説明に助けられました。
http://www.binzume.net/diary/2010-07-24:A3

その他

取得したセンサ値をcanvasに描画する際は下記サイトを参考に。
http://ichitcltk.hustle.ne.jp/gudon/modules/pico_rd/index.php?content_id=80

描画の更新は、View#invalidate() を使用。
http://www.hakkaku.net/articles/20090206-341

ARアプリで磁気偏角の補正は必要でしょうか?今後確認します。
http://ameblo.jp/seccager/entry-10898411084.html

Xperiaのバッテリー節約について

最近急にXperiaの電池の持ちが悪くなって、他のサイトとかも参考にしながら2,3日いろいろ調べてたらだんだん混乱してきたけど、やっと整理できました。

結論から言うと、作りの悪いアプリとウィジェットが、スリープ中にやたらと電池を食うことが意外と多いみたい。

ずっと何かを監視してたり、ネットに定期的にアクセスする必要があるなら、電池を消費するのはわかるけど、そういうアプリで「定期更新しない」設定にしてても、Pause状態でプロセスが存在するだけでかなり電池を食う場合がありました。
前エントリーで作ったCPU消費をチェックするアプリで見てても、特にCPUは動いて無いのに、なぜか電池を食うんですよね。何かリソースをつかんだままなのか。。。?

#というわけで、作ったアプリはあまり意味が無かった。笑

androidって、いろんなアプリをダウンロードして遊べるのが面白いのに、そのアプリのせいでバッテリーがなくなっちゃうのは困ります。
操作中は問題となるアプリが動いてても問題なくて、スリープ中にそういうアプリが確実に終了してればいいので、タスクキラー系のアプリを使うことにしました。

不要なアプリを明示的に終了させることで、通勤時間や昼休みにtwitterやブラウザを結構使ったり、いろいろアプリダウンロードしたりしても、夕方6時ぐらいにバッテリーが70%ぐらい残るようになりました。これくらいの残量ならかなり安心。

根本的な解決のために、Googleandroid developersサイトの開発ガイドに、省電力なアプリ開発のためのガイドラインを載せて欲しいです。

それでは、参考までに僕の設定を。

前提

・メールはGmailを使用。
・アドレス帳は自動同期せず、編集時だけ手動で同期。
・カレンダーは同期しない。
・Timescapeは使わない。
・プリインストール以外で日常的に使うアプリ
 - CliphWeather : 天気予報ウィジェット
 - twicca : twitterクライアント
 - GoMarks : Googleブックマークへのアクセス
 - Simeji : インプットメソッド
 - Battery Indicator : ステータスバーに電池残量を表示
 - Automatic Task Killer : タスクキラー
 - Advanced Task Manager : タスクキラー

タスクキラーの設定

方針1.

頻繁に使う(&信用できる)アプリ、常駐して無いと意味が無いアプリ、必要そうなプリインストールアプリを残して、それ以外は Advanced Task Manager を使って適当なタイミングで手動で終了。デスクトップにタスク終了用のウィジェットを設置。

Excludeの設定をしたApps: (終了しないもの)

(プリインストール)
  Wiper App
  Face Recognition Ser...
  Service Account Prov...
  ソフトウェア更新
  android.bootinfo
  アラーム
  sonyericsson.learningclient
  Gmail
(インストールしたもの)
  Simeji
  Battery Indicator
  CliphWeather
  Automatic Task Killer
  Advanced Task Manager

Excludeの設定をしたServices: (終了しないもの)

(プリインストール)
  Wiper
  FaceRecognitionServi...
  StkAppService
  service.GTaklService
  BluetoothHeadsetServ...
(インストールしたもの)
  RefleshService (CliphWeatherのアイコン)
  MainService (Automatic Task Killerのアイコン)
  BatteryIndicatorServ... (Battery Indicatorのアイコン)

※AllAppsはいじってません。

方針2.

手動で終了させるのを忘れたときのために、Automatic Task Killer を使ってスリープに入るときに、下記"以外"のアプリを自動で終了。
(終了しないもの)

(プリインストール)
  Gmail
  アラーム
(インストールしたもの)
  Advanced Task Manager
  Battery Indicator
  Simeji
方針3.

プリインストールのアプリで、使うことが無いと思われるものを、 Advanced Task Manager の Auto-End Apps Service を使って終了させる。

Auto-End Frequency : Every 30 mins
Auto-End Running Apps : チェックしない
Auto-End Apps List (終了させるもの)
・Moxier メール
・SNS Provider
・Timescape Plug-in M...
・Timescape Provider
・mixi
・カメラ
 (プリインストールでは2種類あり、撮影用じゃないほう。画像編集系?)
 (アイコンが正面じゃなくて左斜め下向いてるほう)

その他の設定

Wi-Fi : 外ではOFF ※ONだと結構電池消費しそう
・Bluetooth : 常にOFF
・ネットワークモード : WCDMAのみ
・タッチ操作音 : OFF
・選択時の操作音 : OFF
・SDカードの通知 : OFF
・画面の向き : OFF ※あまり影響なし?
・画面の明るさ : 自動調整OFF、明るさ低めに
・バックライト消灯 : 1分
・バックグラウンドデータ : OFF ※あまり影響なし?
・Gmailの同期 : ON
・サービス設定 : Google以外は登録しない
・位置情報(ワイヤレスネットワーク) : ON
・位置情報(GPS機能) : 普段はOFF ※ONでもあまり変わらない?
・Googleと共有 : OFF

Wi-FiGPSはONにしたいときもあるので、デスクトップにプリインストールのクイック設定ウィジェットを置いています。
タスクキラーとクイック設定以外のウィジェットは、置いてません。時計も外して様子を見てます。


---2010/05/14 1:20追記---
1日たったらまた電池の消費が増えた。。。
念のため、下記のアプリも自動停止するようにしました。
上の設定も修正済み。

方針1.

  マップ
  マーケット
  設定
  電話帳

方針2.

  マーケット
  設定
  電話帳