Hatena::ブログ(Diary)

inutchの日記

2017-02-08

D3.js v4

v4の情報

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

  • d3/API.md at master ? d3/d3 ? GitHub
    • 本家のAPIリファレンス。最終的にはこれを地道に読むか。
  • Overview
    • D3.jsの簡単なチュートリアルをv4で。
  • no title
    • かなり細かいところまで踏み込んだe-book。オンラインで読めます。

v3で学習→変更点を理解

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

v3からv4の変更点

おまけ

2017-02-05

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 QGISプロジェクトへようこそ!

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>

f:id:inutch:20170208220659p:image

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.js

D3.js の Data-Driven な DOM 操作がおもしろい - てっく煮ブログ

d3.js チュートリアルまとめ(前半) (D3.js入門(Data-Driven Documents)) | OpenBook

SVG

svg要素の基本的な使い方まとめ

参考

D3.jsで画像を使わず日本地図をブラウザに表示

D3で世界地図を描画してみる - Qiita

D3.jsとOpen Data〜その1地図を描画する - Qiita

Let’s Make a Map

次やること

使っているライブラリのバージョンが低いので、まずは最新に追従しようと思います。

D3 API Reference

その後、地図にいろいろ書き込んだり、インタラクティブにしたり。

2012-03-20

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

「屋外のような外乱影響の少ないところでは、地磁気方向(北)を正確に測定できると思いますが、ビルなどの屋内では鉄筋の影響を受けて磁場が歪んでいるので、正確な地磁気方向を測定することは難しいと思います。」

2012-03-06

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()


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

2012-03-04

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