センサーマップダッシュボード開発
地図はleafletで表示。
簡単で便利。
Google Mapが簡単に使えなくなったので、基本これで良いと思う。
「Mini Tokyo 3D」もleaflet使ってる。
leaflet上にマーカーを置くときは緯度経度を指定するのだが、この際はGoogle Mapでマーカーを置く場所を決め右クリックをして緯度経度をコピーしてプログラムに持ってくると良い。
マーカーのオプションゴニョニョいじれる。
Liquid Fill Gauge
いい感じの液体ゲージがつくれる。
湿度を示すのに良いかと思ったので、採用。
jsで配列の中に特定の要素があるか判定する。
if ( array.findIndex( ({ name }) => name === '探してる要素' )!== -1) {
//見つかった時の処理
}
この辺参考。
'getComputedStyle' of null
よく出るエラー。
基本的に指定したid要素がなかったときとかに出る。
jsのクラス曖昧やった。
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
少数第一位までつかう
Math.round(123.456 * 10) / 10
-----------------
> 123.4
-----------------
Leaflet|マップのポップアップ
L.marker(sensorPopupObjs[i].location)
.addTo(mymap)
.bindPopup(`<div id="${sensorPopupObjs[i].name}"></div>`, {
autoClose: false,
closeButton: false,
className: "popup",
})
.on("popupopen", () => {
sensorPopupObjs[i].setActive(true);
sensorPopupObjs[i].createGaugePopup();
})
.on("popupclose", () => {
sensorPopupObjs[i].setActive(false);
})
.openPopup();
popupopen, popupcloseとかのイベントごとに処理を登録しておくと良い。
clickイベントでやる必要はない。popupopenが結局呼ばれるので。
EDM聞きながら作業捗る。良い。
セルシウス温度を色温度likeなもの(青から赤にグラデーションする)に変換するコード書いた。
p5jsのmap function的な処理をしている。
const maxTemperature = 40;
const minTemperature = -5;
const p5LikeMapFunc = (n, start1, stop1, start2, stop2) => {
return ((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2;
};
const temperatureBase = p5LikeMapFunc(
ここに温度,
minTemperature,
maxTemperature,
0,
255
);
config.circleColor ="rgb(" + [temperatureBase, 128, 255 - temperatureBase].join(",") + ")";
ケルビン温度を本物の色温度に変換するコードはいっぱい転がっている。セルシウス温度→ケルビン温度にして、無理やり使うのもあり。
でも自分には色温度がわかりやすいと思えないので、色温度likeなグラデーションを採用。
追記です。
いい感じに外部モジュール化した。
function bToRColorTemperatureDefaultSettings() {
return {
maxTemperature: 40,
minTemperature: -5,
};
}
function getBToRColorTemperature(temperature, config) {
if (config == null) config = bToRColorTemperatureDefaultSettings();
const temperatureBase = Math.round(
((temperature - config.minTemperature) /
(config.maxTemperature - config.minTemperature)) *
255
);
return "rgb(" + [temperatureBase, 128, 255 - temperatureBase].join(",") + ")";
}
// how to use
// const config = bToRColorTemperatureDefaultSettings();
// config.maxTemperature = 50;
// alert(getBToRColorTemperature(40, config));
//or just use like this
// alert(getbToRColorTemperature(40));
オープンソースのリソースとして公開する練習につかいたい。
文字を選択されなくする。
user-select:none
"Magnific Popup"を使う。
いい感じのモーダルポップアップを簡単につくれる。レスポンシブ対応らしい。
ポップアップのスライド対応までしているのが良い。
簡単に使ってみることには成功したので、inlineコンテントを作ってギャラリーに表示する手法でやってみようと思う。
参考サイト
leafletで他のマーカーを開いた時に勝手にマーカーが閉じてしまう挙動に悩まされていたけど、解決。
autoClose: false,
closeOnClick: false,
この2つのオプションが必要。autoCloseだけでもだめ。マップ内のクリックで閉じてしまうので。
jsonでローカルのconfigファイルを読み込めたりしたら綺麗で良いと思ったけどCORSエラーとかで面倒そうだったので、jsファイルでデータそのものを定義することにした。
Magnific Popup難しかった。なんとかinlineのギャラリーを作れた。
まずはhtmlのどっかにモーダルで表示する要素を準備しておく。これは後から動的に生成してもよい。このために#modal-itemsで要素をまとめて管理している。
<div id="modal-items">
<div id="modal-1" class="mfp-hide white-popup">
item-1
</div>
<div id="modal-2" class="mfp-hide white-popup">
item-2
</div>
<div id="modal-3" class="mfp-hide white-popup">
item-3
</div>
</div>
次に選択されるとモーダルを発火させる要素を準備しておく。ここは単純に.modal-linkで囲ってやるだけで大丈夫。さらにそれぞれの子要素はaタグで囲い、hrefで先程定義したモーダルで表示する要素を指定する。選択するときの数字部分を動的にすれば、要素も増やせる。
<div iclass="modal-link">
<a href="#modal-1">モーダル1を開く<a/>
<a href="#modal-2">モーダル2を開く<a/>
<a href="#modal-3">モーダル3を開く<a/>
</div>
最後にjsスクリプト。モーダルを発火させる親要素である.modal-linkを指定して、それぞれのモーダルの指定はその子要素のaタグにしている。galleryのenableをtrueにすることでギャラリー表示できる。その他、モーダル開閉時や生成時のコールバックも指定できる。
$(".modal-link").magnificPopup({
delegate: "a",
type: "inline",
gallery: {
enabled: true,
},
callbacks: {
open: function () {},
close: function () {},
},
});
モーダルにチャート用のcanvasを埋め込むと、チラつきが気になる。これはもとからcanvas分の領域をcssで確保してあげると解決。
.white-popup {
position: relative;
background: #fff;
padding: 40px;
width: auto;
min-height: 300px;
max-width: 50%;
margin: 20px auto;
text-align: center;
user-select: none;
}
min-height: 300px;の記述が大事。
ギャラリー表示のモダールに合わせて、地図も動かすのに成功した。
やり方はモーダル要素に情報を埋め込んで、それをmagnificPopupのchangeコールバックで取得する。今回はhiddenのinputに緯度経度のデータを埋め込んでおいた。
change: function () {
//モダールのスライドに合わせてマップの中心の移動
map.setView([
this.content.find(".longitude").val(),
this.content.find(".latitude").val(),
]);
},
MITライセンスでソースを公開してみた
chartライブラリ探し
三軸以上の複合チャートが描けるものを探してます。マウスカーソルを重ねたら、そのグラフの単位が表示されるみたいのが好ましい。縦軸は自動でスケールしてほしい。
なかなか良さそうなのが見つからない...。
二酸化炭素濃度の基準
特に色に決まりはないらしい。
d3.jsがバージョン間で互換性がなさすぎるかつ、複数バージョンの共存は難しそうなので、d3jsベースのグラフライブラリc3jsは断念。特にver3から4は大きい違いらしい。他も違うかもだけど。
代わりに最も検索に引っかかったchartjsを採用。こっちはcanvasベース。c3はsvg。
jsのthisではまった〜
なかなか体で理解できん。
関数呼び出しパターンは一回selfでthisを受けてから使うと。
var myObject = {
value: 1,
show: function() {
var self = this;
console.log(self.value); // 1
function show() {
console.log(self.value); // 1
}
show();
}
};
myObject.show();
chart.jsでリアルタイムチャート
基本的にonRefreshが重要。ここで新たなデータをpushしていく。
xAxes: [
{
type: "realtime",
realtime: {
duration: 86400000,
refresh: 60000,
delay: 120000,
onRefresh: function (chart) {
chart.data.datasets[0].data.push({
x: Date.now(),
y: self.temperature1,
});
},
},
},
],
chart.jsでのリアルタイムチャートが異常に重いので原因を調べてる。
そもそもこのライブラリの問題説もある。ver3で解決するとか書いてある。
ToDo
canvas生成時にガタつく問題の修正
日本語配列のmacをus配列として使うとグレイヴ・アクセントが使えないので、苦肉の策として日本語入力の辞書に'をグレイヴ・アクセント追加した。
もっと良い方法はあるのだろうか...
リアルタイムチャート表示だったらepoch jsってのもある。
ある程度長い時間動かしていると重くなる原因がたぶんわかった。
D3 Liquid Fill Gaugeライブラリのこの部分。GaugeUpdaterってのをクリエイトのたびにnewしているからだ。
普通のアップデートの際にはこれをつかってupdateすればよいのだが、configに含まれるゲージの色もアップデートしたいので毎回createする必要がある。
結論として、今回はこの関数とreturnをまとめてコメントアウトすることで重さを改善。
function GaugeUpdater() {
// ゲージのアップデートをしてくれる関数
this.update = function (value) {
};
}
// return new GaugeUpdater();
メモリリーク問題
- chromeでconsole.logを出し続けると重くなるくさいのでそれをコメントアウト
- chartjsの表示データをずっとpushし続けるのはよくないので、表示可能最大数に達したらshift()をするようにした。shiftが重そうで怖い。ここは実際に動かして試さないと。