地球から2次元地図への遷移のアニメーション

地図作成の実験をHabrと共有したいと思います。つまり、 投影(球)から等距離 (通常の2次元地図の投影の1つ)への遷移のアニメーションです。 また、この方法は他の投影にも適しています。 実験の結果はそのようなアニメーションでした:



地球から地図へ








前と同じように、 d3.jsライブラリを使用します。前と同じように、 SVGとCanvasといういくつかの実装を作成します。 両方のオプションは、インタラクティブなインフォグラフィックに効果的に使用できます。 さあ、始めましょうか?



開始する



私はかつてテレビ(Gazpromなど)で広告を見たことがあります。この広告では、地球がただ回転してから地図に展開します。 美しい漫画、私はそれが好きでした、よく、私は似たようなことをするだけで、インタラクティブでインターネットのために決めました。 チャレンジを受け入れました



単純なものから複雑なものまで



すでにマップ( d3.jsを使用したインタラクティブSVGマップ )とグローブ( インタラクティブグローブ -SVG 対Canvas )で作業しているため、ある投影から別の投影への移行からすぐに始めます。 最初に思い浮かぶのは、トランジションアニメーションが既にライブラリに実装されているため、すべてをライブラリに渡すことです。 だから私はやった。 最初の例の完全なコードは、 GitHubGlobe to Mapで見ることができ、 bl.ocks.orgで感じることができます: Globe to Map 。 はい、 bl.ocks.orgで左上隅のブロック番号クリックすると、 GitHubの対応する要点に移動するため、将来的にはリンクを1つだけ提供します。



つまり、フォーカスとは国に焦点を当てる指標、 ortho



は投影の指標、速度は回転速度、 start



は回転のstart



corr



は回転フェーズを保存する変数です。



endall(transition, callback)



関数は、トランジション(アニメーション)が適用される要素の数をカウントし、すべてが終了すると、それに渡された関数( callback



)を実行します。 原則として、 SetTimeout



に置き換えることができますが、 endall(transition, callback)



を使用する方が適切です。



 //Starter for function AFTER All transitions function endall(transition, callback) { var n = 0; transition .each(function() { ++n; }) .each("end", function() { if (!--n) callback.apply(this, arguments); }); }
      
      





回転はd3.timerを使用して実装され、 ortho



クラスのpath



のみ適用されpath







  //Globe rotating via timer d3.timer(function() { var λ = speed * (Date.now() - start), φ = -5; projection.rotate([λ + corr, φ]); g.selectAll(".ortho").attr("d", path); });
      
      





corr



変数を使用すると、投影の変更前と同じグローブの回転角度に戻すことができます。 経度λ



と緯度φ



、Wiki記事の地理座標からの次の図がわかります。



座標球








地球から地図への遷移のアニメーション:



 //Transforming Globe to Map if (ortho === true) { corr = projection.rotate()[0]; // <- save last rotation angle g.selectAll(".ortho").classed("ortho", ortho = false); projection = projectionMap; path.projection(projection); g.selectAll("path").transition().duration(3000).attr("d", path); }
      
      





ここで、現在の回転角度を保存してから、 ortho



クラスを削除して回転を停止し、投影を変更して、組み込みのトランジションアニメーションをオンにします。



逆遷移は、遷移の最後にすべてのortho



クラスを追加し、グローブの回転角度をリセットすることで区別されます(タイマーはこの間ずっと刻々と変化します)。



 //Transforming Map to Globe projection = projectionGlobe; path.projection(projection); g.selectAll("path").transition() .duration(3000).attr("d", path) .call(endall, function() { g.selectAll("path").classed("ortho", ortho = true); start = Date.now(); // <- reset start for rotation });
      
      





質問の残りのコードは、理論的には、特に私の以前の記事を読んでいる場合は、引き起こすべきではありません。



最初の例をしばらく試してみた場合、マップが毎回異なるように見えることに気付くはずです。 これは、地球のさまざまな回転角( λ



)で遷移を開始するため、マップは経度(経線)のさまざまな値にカットされます。 マップの通常の外観(反子午線に沿ったセクション)を取得するには、移行前に地球儀をゼロ子午線に合わせる必要があります。 また、折り目の代わりに、2次元マップの投影パラメーターを変更できますが、最初のオプションを選択しました。 2番目の例では、反時計回りの切断が実装され、グローブがマウスで回転します(ドラッグイベント)。 defaultRotate()



関数はdefaultRotate()



担当します:



 //Rotate to default before animation function defaultRotate() { d3.transition() .duration(1500) .tween("rotate", function() { var r = d3.interpolate(projection.rotate(), [0, 0]); return function(t) { projection.rotate(r(t)); g.selectAll("path").attr("d", path); }; }) };
      
      





インタラクティブグローブ-SVGとCanvasの記事で同様の機能を既に説明しているので、繰り返しません。 2番目の例のコードはbl.ocks.org:Globe to Map IIです。



アニメーションを複雑にする時が来ました。最初の2つの例の変形はクールに見え、存在する権利がありますが、もっと美的なものが欲しいです。



美しさが世界を救うなら、なぜそれは常にいくらかの犠牲を必要とするのですか? ©



この場合、被害者はコードの複雑さを意味します。 そのため、ある投影から別の投影への遷移が最短パスに沿って「愚かに」進まないようにするために、美しく、投影の独自の補間を作成する必要があります。 幸運なことに、Mike: Orthographic to Equirectangularから適切な例を見つけました。 最小限の修正で、両方向のトランジションに使用できます。必要なものだけです。 最終的な実装は次のとおりです。



 //Unreelling transformation function animation(interProj) { defaultRotate(); g.transition() .duration(7500) .tween("projection", function() { return function(_) { interProj.alpha(_); g.selectAll("path").attr("d", path); }; }) } function interpolatedProjection(a, b) { var projection = d3.geo.projection(raw).scale(1), center = projection.center, translate = projection.translate, clip = projection.clipAngle, α; function raw(λ, φ) { var pa = a([λ *= 180 / Math.PI, φ *= 180 / Math.PI]), pb = b([λ, φ]); return [(1 - α) * pa[0] + α * pb[0], (α - 1) * pa[1] - α * pb[1]]; } projection.alpha = function(_) { if (!arguments.length) return α; α = +_; var ca = a.center(), cb = b.center(), ta = a.translate(), tb = b.translate(); center([(1 - α) * ca[0] + α * cb[0], (1 - α) * ca[1] + α * cb[1]]); translate([(1 - α) * ta[0] + α * tb[0], (1 - α) * ta[1] + α * tb[1]]); if (ortho === true) {clip(180 - α * 90);} return projection; }; delete projection.scale; delete projection.translate; delete projection.center; return projection.alpha(0); }
      
      





一見複雑に見えますが、実際はそうではありません。 ここで何が起こっていますか? interpolatedProjection(a, b)



関数に2つの投影を供給し、それらを正規化(1スケール、ラジアン単位の座標)してから、結合投影が作成されます。これは、係数α



(補間ステップ)を持つ元の投影のパラメーター(中心、オフセット)のペアワイズ和です。 そして、各ステップで、 α



に応じて結合投影が与えられます。 ステップα



大きくすると、最初の投影の重みが小さくなり、2番目の投影の重みが大きくなります。 したがって、美しいアニメーションが得られます。

bl.ocks.orgの最終バージョン: Globe to Map III



最後に、キャンバスを使用してバージョンを作成することも決定しました。 このバージョンは、すべての関数がready(error, world, countryData)



内に移動するという点で異なりready(error, world, countryData)



これらはジオデータを直接操作する必要があるためです。それ以外の場合、作業のロジックは同じです。 コメントする特別なものはないので、 bl.ocks.orgのコードは次のとおりです。Globe to Map IV



結果



インタラクティブなマップに興味深い空白を追加しました。これに情報を固定したり、ループして同じ漫画を作成したりできます。 シャドウ、グラデーション、その他の効果を追加することで、視覚化をより洗練させることもできます。 そのようなホイッスルの例を次に示します。





回路図の上に衛星写真やタイルを追加して、すべてをより現実的にすることができますが、これは別の記事のトピックであり、インフォグラフィックにはほとんど適していません。



これで実験が終了します。 最後まで読んでくれた人に感謝します。 皆さん、おもしろいプロジェクトに幸運を。



All Articles