d3.jsを使用してblackHole.jsデータを視覚化するための独自のライブラリである、特定のポイントに持ち込まれたすべての注意を喚起したいと思います。
このライブラリを使用すると、同様の計画の視覚化を作成できます。
クリック可能な写真
または
この記事では、 blackHole.jsをleaflet.jsなどのmapboxと組み合わせて使用する例に専念します。
ただし、使用方法も考慮されます: google maps 、 leaflet.heat 。
このようになります=)
特定の時点に従って、ポイントの動作はグーグルでいた場所によって異なります
見て、どうやって動いたの?...
この例は、 @ theopolismeの location-history-visualizerプロジェクトに基づいています。
記事の本文では、興味深い場所のみが整理され、 codepen.ioの残りのコードを「掘り下げる」ことができます。
記事で
準備する
最初に必要なもの:
- leaflet.jsは、Vladimir Agafonkin(CloudMade)によってJavaScriptで記述されたオープンソースライブラリで、Webサイト(©wikipedia)に地図を表示するように設計されています。
- Leaflet.heatは、リーフレット用の軽量ヒートマップファイルです。
- Google Maps Api -Googleマップのパーソナライズされたマップを接続するため
- Pavel ShramovによるLeafletプラグイン -プラグインを使用すると、 google、yandex、bingカードをleaflet.jsに接続できます。 しかし、特に、 Google.jsスクリプトのみが必要です
- d3.jsは、データを操作するためのライブラリであり、それらを操作するためのツールセットとそれらを表示するためのメソッドセットを備えています。
- まあ、実際にはblackHole.js
- あなたの位置データは、Googleによって慎重に収集されています。
データをアップロードする方法開始するには、 Google TakeoutにアクセスしてLocationHistory情報をダウンロードする必要があります 。 ページで[ 選択なし ]をクリックし、リストで[ ロケーション履歴 ]を見つけて確認します。 [ 次へ ]ボタンをクリックし、[ アーカイブの作成 ]ボタンをクリックします。 作業の完了を待ちます。 [ ダウンロード ]ボタンをクリックして、アーカイブを必要なディレクトリに解凍します。
この例は、index.html、index.css、index.jsの3つのファイルで構成されています。
codepen.ioで見ることができる最初の2つのコード
しかし、一言で言えば、このDOM構造が実際に必要だと言えます。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <div id="map"></div> <!-- --> </body> </html>
JSアプリケーション
アプリケーション自体はいくつかの部分で構成されています。
リーフレットのblackHoleのクラスラップ
blackHole.jsとleaflet.jsを一緒に使用するために、マップの上に視覚化を表示するラッパーレイヤーを作成する必要があります。 同時に、マップを操作するためのすべてのメカニズムとblackHole.jsライブラリのインタラクティブ機能を保持します。
leaflet.jsライブラリには、必要なツールL.Classが含まれています。
その中で、メソッドinitialize 、 onAdd 、 onRemove 、 addToを「オーバーロード」する必要があります。
実際、これらはleaflet.jsのレイヤーでの標準的な作業のためのメソッドにすぎません 。
説明付きのクラス
!function(){ L.BlackHoleLayer = L.Class.extend({ // initialize: function () { }, // onAdd: function (map) { // , if (this._el) { this._el.style('display', null); // if (this._bh.IsPaused()) this._bh.resume(); return; } this._map = map; // div, // this._el = d3.select(map.getPanes().overlayPane).append('div'); // blackHole this._bh = d3.blackHole(this._el); // div var animated = map.options.zoomAnimation && L.Browser.any3d; this._el.classed('leaflet-zoom-' + (animated ? 'animated' : 'hide'), true); this._el.classed('leaflet-blackhole-layer', true); // map.on('viewreset', this._reset, this) .on('resize', this._resize, this) .on('move', this._reset, this) .on('moveend', this._reset, this) ; this._reset(); }, // leaflet onRemove: function (map) { // . this._el.style('display', 'none'); // , if (this._bh.IsRun()) this._bh.pause(); }, // . addTo: function (map) { map.addLayer(this); return this; }, // resize _resize : function() { // . this._bh.size([this._map._size.x, this._map._size.y]); this._reset(); }, // _reset: function () { var topLeft = this._map.containerPointToLayerPoint([0, 0]); var arr = [-topLeft.x, -topLeft.y]; var t3d = 'translate3d(' + topLeft.x + 'px, ' + topLeft.y + 'px, 0px)'; this._bh.style({ "-webkit-transform" : t3d, "-moz-transform" : t3d, "-ms-transform" : t3d, "-o-transform" : t3d, "transform" : t3d }); this._bh.translate(arr); } }); L.blackHoleLayer = function() { return new L.BlackHoleLayer(); }; }();
これに関してそれほど複雑なことはなく、 leaflet.jsのプラグイン、レイヤー、またはコントロールはこの方法で作成されます。
blackHole.jsの視覚化コントロールの例を次に示します。
Google Maps Personalization
Google Maps APIは、表示された地図をカスタマイズする機能を提供します。 これを行うには、 ドキュメントを読むことができます 。 多くのパラメータとそれらの組み合わせがあり、希望する結果が得られます。 しかし、すぐに既製のキットを使用してください 。
それでは、地図を作成して、必要なスタイルのGoogleタイトルをリクエストしましょう。
Googleマップのアップロードコード
// div#map var map = new L.Map('map', { maxZoom : 19, // minZoom : 2 // }).setView([0,0], 2); // // google c ROADMAP . var ggl = new L.Google('ROADMAP', { mapOptions: { backgroundColor: "#19263E", styles : [ { "featureType": "water", "stylers": [ { "color": "#19263E" } ] }, { "featureType": "landscape", "stylers": [ { "color": "#0E141D" } ] }, { "featureType": "poi", "elementType": "geometry", "stylers": [ { "color": "#0E141D" } ] }, { "featureType": "road.highway", "elementType": "geometry.fill", "stylers": [ { "color": "#21193E" } ] }, { "featureType": "road.highway", "elementType": "geometry.stroke", "stylers": [ { "color": "#21193E" }, { "weight": 0.5 } ] }, { "featureType": "road.arterial", "elementType": "geometry.fill", "stylers": [ { "color": "#21193E" } ] }, { "featureType": "road.arterial", "elementType": "geometry.stroke", "stylers": [ { "color": "#21193E" }, { "weight": 0.5 } ] }, { "featureType": "road.local", "elementType": "geometry", "stylers": [ { "color": "#21193E" } ] }, { "elementType": "labels.text.fill", "stylers": [ { "color": "#365387" } ] }, { "elementType": "labels.text.stroke", "stylers": [ { "color": "#fff" }, { "lightness": 13 } ] }, { "featureType": "transit", "stylers": [ { "color": "#365387" } ] }, { "featureType": "administrative", "elementType": "geometry.fill", "stylers": [ { "color": "#000000" } ] }, { "featureType": "administrative", "elementType": "geometry.stroke", "stylers": [ { "color": "#19263E" }, { "lightness": 0 }, { "weight": 1.5 } ] } ] } }); // . map.addLayer(ggl);
その結果、まさにそのようなカードを取得します
MapBoxプロジェクトでしばらく使用した後、このソリューションに着きました。これは、マップなどを便利にスタイリングするためのツールを提供しますが、より多くのリクエストがあれば支払われます。
ヒートマップ
ヒートマップまたはヒートマップを使用すると、特定の座標に言及する頻度を表示して、スケーリング時に色とグループデータのグラデーションで強度を強調することができます。 そのようなことが判明
ビルドするには、 leaflet.heatmapプラグインを使用します。 しかし、 他にもあります 。
視覚化が常に他のレイヤーの上、特にヒートマップの上にあり、インタラクティブ機能を失わないようにするには、プラグインの他のレイヤーがマップに追加された後にblackHole.jsを追加する必要があります。
// blackHole.js var visLayer = L.blackHoleLayer() , heat = L.heatLayer( [], { // heatmap opacity: 1, // radius: 25, // blur: 15 // }).addTo( map ) // heatmap ; visLayer.addTo(map); // blackHole.js
データの準備と視覚化
このライブラリは、特定のデータ形式ですぐに使用できる状態になっています。
var rawData = [ { "key": 237, "category": "nemo,", "parent": { "name": "cumque5", "key": 5 }, "date": "2014-01-30T12:25:14.810Z" }, //... ]
次に、視覚化を実行するには、jsコードには何も必要ありません。
var data = rawData.map(function(d) { d.date = new Date(d.date); return d; }) , stepDate = 864e5 , d3bh = d3.blackHole("#canvas") ; d3bh.setting.drawTrack = true; d3bh.on('calcRightBound', function(l) { return +l + stepDate; }) .start(data) ;
ドキュメントの詳細
しかし、たまたま理想的なケースが存在する世界に住んでいることがあります。
したがって、このライブラリにより、プログラマーはデータフォーマットを操作 するためにblackHole.js を準備することができます。
このケースでは、GoogleのLocationHistory.jsonを扱っています。
{ "somePointsTruncated" : false, "locations" : [ { "timestampMs" : "1412560102986", "latitudeE7" : 560532385, "longitudeE7" : 929207681, "accuracy" : 10, "velocity" : -1, "heading" : -1, "altitude" : 194, "verticalAccuracy" : 1 }, { "timestampMs" : "1412532992732", "latitudeE7" : 560513299, "longitudeE7" : 929186602, "accuracy" : 10, "velocity" : -1, "heading" : -1, "altitude" : 203, "verticalAccuracy" : 2 }, //... ]}
データを準備し、それを使用するようにblackHole.jsを設定しましょう。
スタート/リスタート機能
function restart() { bh.stop(); if ( !locations || !locations.length) return; // heatmap heat.setLatLngs([]); // bh.start(locations, map._size.x, map._size.y, true); visLayer._resize(); }
データの解析中
ファイル読み取り機能とデータ準備
var parentHash; // . function stageTwo ( file ) { bh.stop(); // // LocationHistory leaflet.js var SCALAR_E7 = 0.0000001; // processFile( file ); function processFile ( file ) { // FileReader var reader = new FileReader(); reader.onprogress = function ( e ) { // }; reader.onload = function ( e ) { try { locations = JSON.parse( e.target.result ).locations; if ( !locations || !locations.length ) { throw new ReferenceError( 'No location data found.' ); } } catch ( ex ) { // console.log(ex); return; } parentHash = {}; // var sw = [-Infinity, -Infinity] , se = [Infinity, Infinity]; locations.forEach(function(d, i) { d.timestampMs = +d.timestampMs; // // d.lat = d.latitudeE7 * SCALAR_E7; d.lon = d.longitudeE7 * SCALAR_E7; // parent d.pkey = d.latitudeE7 + "_" + d.longitudeE7; // sw[0] = Math.max(d.lat, sw[0]); sw[1] = Math.max(d.lon, sw[1]); se[0] = Math.min(d.lat, se[0]); se[1] = Math.min(d.lon, se[1]); // , . d.parent = parentHash[d.pkey] || makeParent(d); }); // locations.sort(function(a, b) { return a.timestampMs - b.timestampMs; }); // id locations.forEach(function(d, i) { d._id = i; }); // map.fitBounds([sw, se]); // restart(); }; reader.onerror = function () { console.log(reader.error); }; // reader.readAsText(file); } } function makeParent(d) { var that = {_id : d.pkey}; // leaflet that.latlng = new L.LatLng(d.lat, d.lon); // // that.x = { valueOf : function() { var pos = map.latLngToLayerPoint(that.latlng); return pos.x; } }; that.y = { valueOf : function() { var pos = map.latLngToLayerPoint(that.latlng); return pos.y; } }; return parentHash[that.id] = that; }
valueOf関数を設定してオブジェクトの値を取得できるため、マップ上の親オブジェクトの正確な座標を常に取得できます。
blackHole.jsのセットアップ
// bh.setting.increaseChild = false; bh.setting.createNearParent = false; bh.setting.speed = 100; // bh.setting.zoomAndDrag = false; bh.setting.drawParent = false; // parent bh.setting.drawParentLabel = false; // bh.setting.padding = 0; // bh.setting.parentLife = 0; // bh.setting.blendingLighter = true; // Canvas bh.setting.drawAsPlasma = true; // bh.setting.drawTrack = true; // var stepDate = 1; // // , , (d) bh.on('getGroupBy', function (d) { // return d._id //d.timestampMs; }) .on('getParentKey', function (d) { return d._id; // }) .on('getChildKey', function (d) { return 'me'; // , }) .on('getCategoryKey', function (d) { return 'me; // , }) .on('getCategoryName', function (d) { return 'location'; // }) .on('getParentLabel', function (d) { return ''; // }) .on('getChildLabel', function (d) { return 'me'; // }) .on('calcRightBound', function (l) { // . return l + stepDate; }) .on('getVisibleByStep', function (d) { return true; // }) .on('getParentRadius', function (d) { return 1; // }) .on('getChildRadius', function (d) { return 10; // }) .on('getParentPosition', function (d) { return [dx, dy]; // }) .on('getParentFixed', function (d) { return true; // }) .on('processing', function(items, l, r) { // heatmap setTimeout(setMarkers(items), 10); }) .sort(null) ; // heatmap function setMarkers(arr) { return function() { arr.forEach(function (d) { var tp = d.parentNode.nodeValue; // heatmap heat.addLatLng(tp.latlng); }); } }
ライブラリの仕組み。 起動時に、親と子の一意の要素を識別することにより、提供されたデータを分析します。 getGroupByイベントに渡された関数に従って、視覚化の境界を定義します。 次に、 2つのd3.layout.forceを開始します。1つは親要素の位置を計算し、もう1 つは子要素の位置を計算します。 衝突を解決し、親要素に従ってクラスタリングするためのメソッドは、子要素に引き続き適用されます。
このセットアップでは、次の動作が得られます。
- 100ミリ秒後に発生する各ステップ( bh.setting。Speed = 100 )で、ライブラリはソースデータから要素を1つだけ選択します。
- 親要素に対する相対的な位置を計算します。
- 描画を開始し、次のステップに進みます。
子が1人いるので、親から別の親に飛んでいきます。 そして、それは記事の一番最初に示されている写真であることがわかります。
おわりに
ライブラリは、 GitHub Visualizerの公開後、さまざまなニーズに合わせてリメイクするように多くの注文が出てきたため、独自の問題を解決するために作成されました。
その結果、別のライブラリでGitHub Visualizerに似たビジュアライゼーションを作成するために必要なものをすべて取り除いて、すでにいくつかのプロジェクトを完了しました。
code_swarmの実行時に受信したxmlファイルで動作するblackHole.jsで実際に簡略化されたGitHub Visualizerをここで感じることができます 。
このガイドを使用してファイルを生成できます。
改善を行い、私の間違いを修正する共著者がいることを願っています。
現時点では、ライブラリは4つのコンポーネントで構成されています。
近い将来、パーサーとレンダーを別々のクラスに配置して、データの準備作業を容易にし、キャンバスだけでなく、必要に応じてWebGLで描画する機能を提供する予定です。
役に立つコメントを待っています!
よろしくお願いします!
PSフレンズ、プライベートメッセージのエラーについて書いてください。