Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

JavaScriptで使えるグラフ描画ライブラリとその有効活用例について

f:id:fa11enprince:20200327025625j:plain ふとニュースなどをみていて、新型コロナウィルスの感染状況のグラフのサイトを見つけて、お、綺麗だしシンプルでいいなと思ったのがここ。
よくある質問の部分も、一見、グラフを眺めていて、ん???と思う疑問点が書かれてあって良い。

「具体的な基準はMITライセンスに準拠します」とさらりと書いてあるだけなのがクール。もちろんGitHubへのリンクもあります。 これ厳密に従うとちょっと面倒なんですよね。とはいえ、数あるライセンスの中でも緩い縛りのMITライセンス。
単なるリンクまたはiframeであれば著作権表示はいらないのですが、せっかくなのでスクリーンショットを拝借しました。
ということで厳密に対応してみました。ただ、ライセンスファイルはブログの関係上置けないので、貼り付けました。 f:id:fa11enprince:20200327031353p:plain https://github.com/kaz-ogiwara/covid19/

MIT License

Copyright (c) 2020 Kazuki OGIWARA / 荻原 和樹

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

一応これは何だっていうのを解説しておくと要はMITの場合

  • このソフトウェアを誰でも無償で無制限に扱って良い。ただし、著作権表示および本許諾表示をソフトウェアのすべての複製または重要な部分に記載しなければならない。
  • 作者または著作権者は、ソフトウェアに関してなんら責任を負わない。

という2点が大事です。

本題とはそれますが、こういうのって、いかにデータを集めるかというのが大変ですよね・・・。

Chart.jsいいなと思って、ふとグラフ描画ライブラリって何がいいんだろうなーとふと思いました。 自分が使ったことあるのはPlotly.js。d3.jsを拡張していてインタラクティブで綺麗で、きめ細かい設定が簡単にできて仕事で使ってました。 PythonでもJavaScriptでも使えるし。 が、このChart.jsもいいなと。あと有償ですが有名どころだとHighcharts.jsもありますよね。

代表的なグラフライブラリのリンク

https://www.chartjs.org/
https://plotly.com/javascript/
https://www.highcharts.com/

比較

https://tech.willgate.co.jp/entry/2018/03/20/141000
https://www.npmtrends.com/chart.js-vs-highcharts-vs-plotly.js-vs-taucharts
Chart.js強いなーという感じです。

僕はPlotly.js推しです(というかそれしか使ったことない)。 リファレンスもまぁまぁ充実しているし、細かいことをやろうと思うと、ソースコードを読めば、 あ、これも設定できるんだとわかるし。よかったです。 あれなんですよね。等高線(コンター)図で綺麗なのを描きたかったり、場合によっては3Dにしたいなと思ったので…前回はそれを選んだのかな。 でも、周りでChart.jsのこともよく聞くので、今度使ってみようかなと思いました。

VSCodeでPython3開発環境構築

f:id:fa11enprince:20200211214101j:plain

WindowsでVSCodeとAnaconda3(Python3)で開発環境を整えることにしました。
目標は次の通りです。

  • VSCode上で関数の定義へのジャンプや参照、コード補完ができる
  • docstring(JavaでいうJavadoc)が楽にかける
  • テストのカバレッジ取得及び通ったコードパスが視覚的にわかる

あと、開発環境と違うけれど…

  • loggerの使い方例
  • ソースコードの構成例

を一緒にまとめて置いておきました。
この例では極小の意味のないアプリケーションですが、MongoDBを使うサンプルになっています。 ここに書いてある
requirements.txtによる方法は既に古いらしいので別の方法にしたほうがいいそうです。

補足事項

Pythonの環境切替

この記事のようにAnaconda2 ,3の両方を入れている場合、VSCodeのこの部分から変更ができる
f:id:fa11enprince:20200211215021p:plain
Python 3.7,4 64-bitってところが実はクリックできて、ここで切り替え可能です。

カバレッジのコードハイライト

Coverage Gutters - Visual Studio Marketplace ここのアニメーションの通りなのですが、先ほどと同様に下部のWatch/Remove watchから表示切替ができます。

単体テスト実行について

基本的に該当のフォルダに移動して(PYTHONPATH/モジュール検索パスが通っているところ)で unittest
python -m unitest discoverとやっていたところを
coverage run -m unitest discoverとやればOKです。
ただしunittest前提です。pytestを使っている場合は別の方法になります。

pushd app1\test
coverage run -m unittest discover && coverage xml
popd

discoverってなんだ?と思ったらTestLoader.discover()を呼び出しているそうです。

Windowsを英語化

f:id:fa11enprince:20200131025725j:plain Windowsを英語化してみました。
まだ2日しかたってないですが今のところトラブルなし。
Explorerで最初の一文字目をキーボードで打ってフォーカスがあてられるように
DocumentとかPicturesとか英語のままがよかったので、
英語版にしました。もちろんそこだけ設定いじって英語化する方法もなくはないけど、
Windows Updateのたびに戻されるのは嫌なので、このほうがいいと思い思い切ってWindowsを英語化。
ちなみにトラブルシューティングの際、英語メッセージだとより問題に近づきやすいのでググラビリティも高まるはず!
特に問題は起きてないです。
以前Macも途中から英語化したけどその時はESETでインストールしなおし問題があったよーな…。

参考にさせてもらったところ

に従って英語化
大体これですべて英語になる

設定方法

設定 > 言語
英語を追加し、言語パックをインストール
英語のオプションで  キーボードの追加で日本語を追加してUSのキーボードを消す IMEの関係上、日本語 > 英語の順のまま

Windows表示言語
English(United States)
に変更 Explorerとかはこれで英語になります

アプリ毎の変更

あとはアプリごとに切替が必要です。 Macだと一気に全部英語になりますがWindowsは個別です。 それがいいのか悪いのかは、設計思想だと思うんです。
Windows歴が長いせいもあり、この辺はWindowsのほうが良いのではないかなと…
(変更がめんどくさいけど)

アプリ ざっくり設定方法
Chrome 英語のまま→日本語翻訳を使いたい場合、日本語のままが良い
Firefox 設定 > 一般 > 代替言語 > 英語の追加
Thunderbird オプション > 詳細から英語化
WinMerge オプションから英語に切替
Kindle 言語を切り替えから英語
Evernote オプションから言語を切り替え
Eclipse そもそも英語のままで日本語化してない
Visual Studio Code そもそも英語のままで日本語化してない
ConEmu そもそも英語のままで日本語化してない
WinSCP 英語に切り替わってる
HeidiSQL 英語に切り替わってる
サクラエディタ 日本語のまま(dllを置くと英語になるらしい)
rlogin そもそも英語に対応してないっぽい?のでそのまま
LINE 日本語のまま
ESET 日本語のまま

Googleを英語化した状態で英語を含む他言語を右クリックで日本語にする設定は少し難易度が高かった。
なぜかデフォルトはポルトガル語になっていました。
設定を変えたあと、翻訳のポップアップが出てきたときにいろいろ選ぶとうまくいきます。
(正直ブラウザは日本語設定のままでもいいのかも)
いろいろいじくっていたら二重翻訳とかたまたまできてしまった(日本語→英語→日本語)。

余談

そういやコマンドプロンプトとかそのまま英語になる

> ls
'ls' is not recognized as an internal or external command,
operable program or batch file.

素敵。やったね

Chromeの翻訳について

英語をデフォルトにしたときに英語ページで日本語に翻訳というのは無理っぽい。
ユースケース的に基本的にあり得ないので仕方ない気もするのだが、
そもそもこの辺りをすべて制御できるようなUIにはなっておらず、
ちゃんと設定するには一度、
「母国語以外のページツールで翻訳ツールを表示する」を選択し
母国語(設定言語)以外になっているページを表示すると、
翻訳ツールにどの言語からどの言語に翻訳するかが現れるので
「英語」→「日本語」を選択する。
こうすると、任意の言語から任意の言語へ翻訳できる。
そのあと、「母国語以外のページツールで翻訳ツールを表示する」を解除するとよい。
しかし、改めてよくわからない設定…という感じだ。
この辺りはこのページが詳しい。

Processingで内積・外積の勉強

f:id:fa11enprince:20200121024039j:plain ひょんなことから内積・外積をちゃんと学びなおしたいと思い…
文系であった自分はあまりなじみがないのです。物理も選択せず生物選択でした。
数学は嫌いではないんだけど、計算が得意でなかったです。
とはいえ行列をやっていた関係もあり、全く知らないというわけではないのです。
それに最近避けたいのに避けられない機械学習でもというかnumpyというか、dot、dot、dotばっかりな気がしていますので改めて復習。 グラフィカルに確認したいなと思い、いろいろ見てました。 そもそも何に使うか…

  • 内積:矩形は別の判定方法が簡単に思いつくけど、三角形や円にたいしてある点が当たっているかどうか。(衝突判定)とか2つのベクトルがわかっているときにその角度が鋭角か鈍角か求めたいとき
  • 外積:キャラがジャンプした時の高さを求める

内積や外積が難しいのはそもそも何に使うんよ…ってのが分かりにくいからだと思います。 自分の理解したざっくり定義では、

  • 内積:ある方向に引っ張ったときに元となるベクトルと方向を合わせて水平方向に引っ張れる力や量的なモノ
  • 外積:ある方向に引っ張ったときに元となるベクトルと垂直方向に引っ張れる力や量的なモノ

と理解しました(あってんのか?)てかこれ物理とってたら習う?
ちなみにですが内積については類似度っていう考え方もあるみたいです。

個人的にはここがわかりやすかったです。 基礎の基礎編その1 内積と外積の使い方
理屈だけわかっても仕方ないんで、「内積 衝突判定」でググってみました。
すると、なんかコード例が書いてて素敵!
円と線分の当たり判定を行うには | 自己啓発。人生について考える
なんだこのJavaっぽい言語は…一瞬TypeScriptにも見えるしな…と思い、調べたらProcessingでした。 自分もProcessingで書いてみました。ほうほう…内部でJavaのSwing使ってんのね。
余談ですがProcessing書いてるとPythonとかJavaScript(ES6以降)っぽいなーとうっすら思います。

ソースコード

class Point2D {
  float x;
  float y;
  Point2D(float x, float y) {
    this.x = x;
    this.y = y;
  }
  void disp(color c, float diameter) {
    stroke(c);
    strokeWeight(diameter);
    point(this.x, this.y);
  }
};

class Vector2D {
  float x;
  float y;
  float len;
  Vector2D(float x, float y) {
    this.x = x;
    this.y = y;
  }
  Vector2D(Point2D pointA, Point2D pointB) {
    this.x = pointB.x - pointA.x;
    this.y = pointB.y - pointA.y;
    this.len = dist(pointA.x, pointA.y, pointB.x, pointB.y);
  }
  Vector2D unit() {
    return new Vector2D(this.x / this.len, this.y / this.len);
  }
  float dot(Vector2D vec) {
    return this.x * vec.x + this.y * vec.y;
  }
  float corss(Vector2D vec) {
    return this.x * vec.y - this.y * vec.x;
  }
}

final color BLACK = color(0, 0, 0);
final color WHITE = color(255, 255, 255);
final color BLUE = color(0, 0, 255);
final color GREEN = color(0, 255, 0);
final color RED = color(255, 0, 0);
final float diameter = 20;
final float diameterCenter = 100;
Point2D pointA;
Point2D pointB;
Point2D pointP;

void setup() {
  size(300, 300);
  pointA = new Point2D(20, 20);
  pointB = new Point2D(260, 260);
  pointP = new Point2D(150, 150);
}

void draw() {
  background(WHITE);
  noFill();
  
  stroke(BLACK);
  strokeWeight(2);
  line(pointA.x, pointA.y, pointB.x, pointB.y);
  pointA.disp(BLUE, diameter);
  pointB.disp(GREEN, diameter);

  Vector2D vecAB = new Vector2D(pointA, pointB);
  Vector2D vecAP = new Vector2D(pointA, pointP);
  Vector2D vecBP = new Vector2D(pointB, pointP);

  color c;
  if (isCollision(vecAB, vecAP, vecBP)) {
    drawInnerPointOfCircle(vecAB, vecAP);
    c = RED;
  } else {
    c = BLACK;
  }
  drawCircle(c, pointP);
}

void drawInnerPointOfCircle(Vector2D vecAB, Vector2D vecAP) {
  Vector2D unit = vecAB.unit();
  float len = unit.dot(vecAP);
  Point2D pointX = new Point2D(pointA.x + (unit.x * len), pointA.y + (unit.y * len));
  pointX.disp(color(100,100,255), diameter);
}

void drawCircle(color c, Point2D pointP) {
  strokeWeight(2);
  stroke(c);
  ellipse(pointP.x, pointP.y, diameterCenter, diameterCenter);
}

boolean isCollision(Vector2D vecAB, Vector2D vecAP, Vector2D vecBP) {
  float lenAX = vecAB.unit().dot(vecAP);
  return shortestDistance(lenAX, vecAB, vecAP, vecBP) < diameterCenter / 2;
}

float shortestDistance(float len, Vector2D ab, Vector2D ap, Vector2D bp) {
  if (len < 0 ) {
    return ap.len;
  } else if (len > ab.len) {
    return bp.len;
  } else {
    return abs(ab.unit().corss(ap));
  }
}

void mouseDragged() {
  if (isDragged(pointA, diameter)) {
    pointA.x = mouseX;
    pointA.y = mouseY;
    return;
  }
  if (isDragged(pointB, diameter)) {
    pointB.x = mouseX;
    pointB.y = mouseY;
    return;
  }
}

boolean isDragged(Point2D point, float diameter) {
  return dist(mouseX, mouseY, point.x, point.y) < diameter / 2;
}

スクリーンショット

f:id:fa11enprince:20200121022851p:plain

ちなみにProcessingのダウンロードはこちら https://processing.org/ 最新版だとJava 8が内包されている様子。
JavaFXは外部でメンテナンスだけどSwingは残りましたね…。