leaflet.jsマップにリンクされたBlackHole.js

コミュニティへのご挨拶!



d3.jsを使用してblackHole.jsデータを視覚化するための独自のライブラリである、特定のポイントに持ち込まれたすべての注意を喚起したいと思います。

このライブラリを使用すると、同様の計画の視覚化を作成できます。

クリック可能な写真

画像 または



この記事では、 blackHole.jsleaflet.jsなどのmapboxと組み合わせて使用​​する例に専念します。

ただし、使用方法も考慮されます: google mapsleaflet.heat



このようになります=)



特定の時点に従って、ポイントの動作はグーグルでいた場所によって異なります


見て、どうやって動いたの?...



この例は、 @ theopolismeの location-history-visualizerプロジェクトに基づいています。



記事の本文では、興味深い場所のみが整理され、 codepen.ioの残りのコードを「掘り下げる」ことができます。



記事で





準備する



最初に必要なもの:





この例は、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.jsleaflet.jsを一緒に使用するために、マップの上に視覚化を表示するラッパーレイヤーを作成する必要があります。 同時に、マップを操作するためのすべてのメカニズムとblackHole.jsライブラリのインタラクティブ機能を保持します。

leaflet.jsライブラリには、必要なツールL.Classが含まれています。

その中で、メソッドinitializeonAddonRemoveaddToを「オーバーロード」する必要があります。

実際、これらは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 つは子要素の位置を計算します。 衝突を解決し、親要素に従ってクラスタリングするためのメソッドは、子要素に引き続き適用されます。



このセットアップでは、次の動作が得られます。



子が1人いるので、親から別の親に飛んでいきます。 そして、それは記事の一番最初に示されている写真であることがわかります。





おわりに





ライブラリは、 GitHub Visualizer公開後、さまざまなニーズに合わせてリメイクするように多くの注文が出てきたため、独自の問題を解決するために作成されました。

その結果、別のライブラリでGitHub Visualizerに似たビジュアライゼーションを作成するために必要なものをすべて取り除いて、すでにいくつかのプロジェクトを完了しました。



code_swarmの実行時に受信したxmlファイルで動作するblackHole.jsで実際に簡略化されたGitHub Visualizerここで感じることができます

このガイドを使用してファイルを生成できます。



改善を行い、私の間違いを修正する共著者がいることを願っています。



現時点では、ライブラリは4つのコンポーネントで構成されています。



近い将来、パーサーとレンダーを別々のクラスに配置して、データの準備作業を容易にし、キャンバスだけでなく、必要に応じてWebGLで描画する機能を提供する予定です。



役に立つコメントを待っています!

よろしくお願いします!



PSフレンズ、プライベートメッセージのエラーについて書いてください。



All Articles