Extension:Graph/Interactive Graph Tutorial

From mediawiki.org
Jump to navigation Jump to search
This page is a translated version of the page Extension:Graph/Interactive Graph Tutorial and the translation is 72% complete.
Outdated translations are marked like this.
Other languages:
English • ‎Ripoarisch • ‎Türkçe • ‎español • ‎français • ‎polski • ‎日本語

このチュートリアルではインタラクティブなグラフの作成方法を説明するため、国ごとの出生率の変化を例題とします。表示する年を選ぶスライダーを設定し、地図を使って世界の出生率の分布を示します。このグラフ限定のソースコードは独立したページavailable as a separate wiki pageで調べてください。インタラクティブなグラフのサンドボックスInteractive Graph Sandboxで使い方を実験できます。またVegaの説明文書全文complete Vega documentationにも興味があるかもしれません。

Move the slider to change the year, and hover the mouse over countries to read the rate. Try it !

図形を描くには

まずいくつかの要素を描くところから始めます(marks)。 <graph mode=interactive>...</graph>タグで挟まれた部分がグラフ用コードで、グラフがインタラクティブでなくても見た目は同じです。 インタラクティブなグラフのサンドボックスInteractive Graph Sandboxを開き、以下のコードから<graph>タグ以外の部分をコピーして実験を始めましょう。

ヒント: 実験用にグラフのスペックをVega Editorにコピーするには、編集ではなく「ソースを編集」を押します。

ソースコードを参照
{
  // Vega 2を使って画像の大きさを設定
  "version": 2, "width": 300, "height": 80,
  // 四辺すべてのパディングを同じ値に設定
  "padding": 12,
  // 既定では背景色は透明
  "background": "#edf1f7",
  "marks": [
    {
      // 水平線を引く
      "name": "scrollLine",
      "type": "rule",
      "properties": {
        "enter": {
          "x": {"value": 0},
          "y": {"value": 40},
          "x2": {"value": 300},
          "stroke": {"value": "#000"},
          "strokeWidth": {"value": 2}
        }
      }
    },
    {
      // 三角形を描き、マウスを当てて移動させるように設定
      // あとで呼び出せるように、オブジェクトに名前を付けておく
      "name": "handle",
      "type": "path",
      "properties": {
        "enter": {
          "x": {"value": 200},
          "y": {"value": 40},
          // パスの構文はSVGのパス タグと同値
          "path": {"value": "m-5.5,-10l0,20l11.5,-10l-11.5,-10z"},
          "stroke": {"value": "#880"},
          "strokeWidth": {"value": 2.5}
        },
        "update": {"fill": {"value": "#fff"}},
        // マウスを当てたときのオブジェクトの配色を変更
        "hover": {"fill": {"value": "#f00"}}
      }
    }
  ]
}

IsDragging 信号

ハンドルのオブジェクトをマウスで移動できるように、まずクリックの有無を検出する方法を考えます。そこで信号signalを追加して、クリックすると真に変わり (isDragging) 結果を示す文を表示する (debug) ことにします: For that, lets add a signal that becomes true when an object is clicked (isDragging), and a text mark to show (debug) the result:

ソースコードを参照
"signals": [
  {
    "name": "isDragging",
    "init": false,
    "streams": [
      {"type": "@handle:mousedown","expr": "true"},
      {"type": "mouseup","expr": "false"}
    ]
  },
],

// 「marks」に対して
  {
    "name": "debugIsDragging",
    "type": "text",
    "properties": {
      "enter": {
        "x": {"value": 250},
        "y": {"value": 0},
        "fill": {"value": "black"}
      },
      "update": {"text": {"signal": "isDragging"}}
    }
  }

ハンドルの位置決めの信号

前節でオブジェクトにマウスが当たったかどうか検知したので、次にmousemove信号を追加し、isDragging信号が真のときだけ変化するよう設定します。 作ったばかりの信号は「update」部でハンドルの「x」座標に付与します: We also attach the new signal to the handle's "x" coordinate via "update" section:

ソースコードを参照
// 「signals」に対して
  {
    "name": "handlePosition",
    "init": 200,
    "streams": [
      {
        "type": "mousemove[isDragging]",
        "expr": "eventX()"
      }
    ]
  }

  // 「marks」に付与
  {
    "name": "handle",
    ...
    "update": {
      "x": {"signal": "handlePosition"},
      ...
    },
  },
  {
    "name": "debugHandlePosition",
    "type": "text",
    "properties": {
      "enter": {
        "x": {"value": 250},
        "y": {"value": 14},
        "fill": {"value": "black"}
      },
      "update": {"text": {"signal": "handlePosition"}}
    }
  }

ハンドルの目盛り上の位置の信号を設定

ハンドルの位置をピクセル値で示してもあまり役に立ちません - グラフのハンドルには図に有用な、例えば「年」のような位置を設定するほうが使いやすいからです。Vegaのスケール(目盛り)は、データ(例=年)を画面上の座標に変換したり戻したり(インバート)する便利な仕掛けがあります。 この段階では1960から2013までの線的なスケール「yearsScale」を付け加え、グラフの幅いっぱいにマッピングします(パッディングを除外)。 さらにscaledHandlePosition信号も新たに加え、マウスを置いた座標Xを「年」に変換します。 Vega scales provide a useful mechanism for converting between our data (e.g. years) and the screen coordinates, and back (invert). In this step, we add "yearsScale" linear scale for values 1960..2013, mapping it to the whole width of the graph (excluding padding). We also add a new scaledHandlePosition signal that translates from the mouse X position to the meaningful value in years.

ソースコードを参照
// 「scales」のルート値を付与:
  "scales": [
    {
      "name": "yearsScale",
      "type": "linear",
      "zero": false,
      "domain": [1960, 2013],
      "range": "width"
    }
  ],

// 「signals」に対して付与:
    {
      "name": "scaledHandlePosition",
      "expr": "handlePosition",
      "scale": {"name": "yearsScale","invert": true}
    }

  // 「marks」に付与
    {
      "name": "debugScaledHandlePosition",
      "type": "text",
      "properties": {
        "enter": {
          "x": {"value": 250},
          "y": {"value": 28},
          "fill": {"value": "black"}
        },
        "update": {"text": {"signal": "scaledHandlePosition"}}
      }
    }

「年」の値のクリーンアップ

既に気がついたかもしれませんが、実はハンドルは上記の設定のままではグラフの横幅を超して動かすことができ、間違った結果を表示してしまいます。 またスケールの目盛りは整数ではなく、「年」の値として不釣合いです。 この状態を訂正するため、もう1件、後処理の信号「currentYear」を作り、必要な範囲の整数に設定します。 初期値は意味のある範囲に設定し、ハンドルバーの位置と「yearLabel」をその値に結び付けます。 注意する点は、ハンドルの位置は画面の座標に合わせる必要があるため、「yearsScale」を使って値を逆変換します。 Also, the scaled value is not an integer that we expect of the year value. To fix this, we can introduce one more post-processing signal called "currentYear" to convert it to an integer and limit it to the needed range. We also initialize it to a reasonable value, and we tie both the "yearLabel" and the position of the handle bar back to this value. Note that the handle's position needs to be in screen coordinates, so we have to use the "yearsScale" to convert the value back:

ソースコードを参照
    // 新しい信号:
    {
      "name": "currentYear",
      "init": 2000,
      "expr": "clamp(parseInt(scaledHandlePosition),1960,2013)"
    }

    // yearLabelマークの更新:
    // 「年」を示す新しいマーク
    {
      "name": "yearLabel",
      "type": "text",
      "properties": {
        "enter": {
          "x": {"value": 0},
          "y": {"value": 25},
          "fontSize": {"value": 32},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "steelblue"}
        },
        "update": {"text": {"signal": "currentYear"}}
      }
    },

    // ハンドルのマークを更新:
    {
      "name": "handle",
      "properties": {
        "update": {
          "x": {"scale": "yearsScale","signal": "currentYear"}
        },
      }
    }

クリーンアップして仕上げ

いよいよデバッグ用のマークをすべて削除できます。 また同じステップでスケールを決めるので、handlePositionとscaledHandlePositionの信号も無用です: We also don't need separate handlePosition and scaledHandlePosition signals because scaling can happen in the same step:

ソースコードを参照
{
  // We want to use Vega 2, and specify image size
  "version": 2, "width": 300, "height": 80,
  // Set padding to the same value on all sides
  "padding": 12,
  // By default the background is transparent
  "background": "#edf1f7",

  "signals": [
    {
      "name": "isDragging",
      "init": false,
      "streams": [
        {"type": "@handle:mousedown","expr": "true"},
        {"type": "mouseup","expr": "false"}
      ]
    },
    {
      "name": "scaledHandlePosition",
      "streams": [
        {
          "type": "mousemove[isDragging]",
          "expr": "eventX()",
          "scale": {"name": "yearsScale","invert": true}
        }
      ]
    },
    {
      "name": "currentYear",
      "init": 2000,
      "expr": "clamp(parseInt(scaledHandlePosition),1960,2013)"
    }
  ],

  "scales": [
    {
      "name": "yearsScale",
      "type": "linear",
      "zero": false,
      "domain": [1960, 2013],
      "range": "width"
    }
  ],

  "marks": [
    {
      // draw the year label in the upper left corner
      "name": "yearLabel",
      "type": "text",
      "properties": {
        "enter": {
          "x": {"value": 0},
          "y": {"value": 25},
          "fontSize": {"value": 32},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "steelblue"}
        },
        "update": {"text": {"signal": "currentYear"} }
      }
    },
    {
      // Draw a horizontal line
      "name": "scrollLine",
      "type": "rule",
      "properties": {
        "enter": {
          "x": {"value": 0},
          "y": {"value": 40},
          "x2": {"value": 300},
          "stroke": {"value": "#000"},
          "strokeWidth": {"value": 2}
        }
      }
    },
    {
      // Draw a triangle shape with a hover effect
      // naming objects allows us to reference them later
      "name": "handle",
      "type": "path",
      "properties": {
        "enter": {
          "y": {"value": 40},
          // path syntax is the same as SVG's path tag
          "path": {"value": "m-5.5,-10l0,20l11.5,-10l-11.5,-10z"},
          "stroke": {"value": "#880"},
          "strokeWidth": {"value": 2.5}
        },
        "update": {
          "x": {"scale": "yearsScale","signal": "currentYear"},
          "fill": {"value": "#fff"}
        },
        // Change fill color of the object on mouse hover
        "hover": {"fill": {"value": "#f00"} }
      }
    }
  ]
}

次のステップ

パート2へ続く