Extension:Graph/Interactive Graph Tutorial/es

This page is a translated version of the page Extension:Graph/Interactive Graph Tutorial and the translation is 12% complete.
Outdated translations are marked like this.

En este tutorial crearemos un gráfico interactivo que desplegará índices de fertilidad histórica por país, con una flecha deslizante para seleccionar el año y un mapa para mostrar la distribución de los índices al rededor del mundo. Si quieres investigar solo el código de fuente de este gráfico, está [$url disponible como una wikipágina separada]. Puede que te interese la documenación Vega completa. If you want to investigate the source code of this graph only, it's available as a separate wiki page. Use Interactive Graph Sandbox for experimentations. You might also be interested in the complete Vega documentation.


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

Dibujando formas

Empezamos dibujando unos pocos elementos (marcas). El código del gráfico está rodeado por las etiquetas <graph mode=interactive>...</graph>, aunque el gráfico todavía no es todavía interactivo. The graph code is surrounded by the <graph mode=interactive>...</graph> tag, even though the graph is not yet interactive. Open Interactive Graph Sandbox and copy the following code without the <graph> tag to experiment.

Pista: Usa "Editar código" para copiar la especificación del gráfico al Editor Vega para experimentar.

ver código fuente
{
  // 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",
  "marks": [
    {
      // 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": {
          "x": {"value": 200},
          "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": {"fill": {"value": "#fff"}},
        // Change fill color of the object on mouse hover
        "hover": {"fill": {"value": "#f00"}}
      }
    }
  ]
}

isDragging signal

To make our handle object dragable, we first need to figure out if it was clicked or not. For that, lets add a signal that becomes true when an object is clicked (isDragging), and a text mark to show (debug) the result:

ver código fuente
"signals": [
  {
    "name": "isDragging",
    "init": false,
    "streams": [
      {"type": "@handle:mousedown","expr": "true"},
      {"type": "mouseup","expr": "false"}
    ]
  },
],

// in "marks"
  {
    "name": "debugIsDragging",
    "type": "text",
    "properties": {
      "enter": {
        "x": {"value": 250},
        "y": {"value": 0},
        "fill": {"value": "black"}
      },
      "update": {"text": {"signal": "isDragging"}}
    }
  }

handlePosition signal

Now that we know when the object is being dragged, we add a mousemove signal that only changes its value when isDragging signal is true. We also attach the new signal to the handle's "x" coordinate via "update" section:

ver código fuente
// in "signals"
  {
    "name": "handlePosition",
    "init": 200,
    "streams": [
      {
        "type": "mousemove[isDragging]",
        "expr": "eventX()"
      }
    ]
  }

  // add in "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"}}
    }
  }

Scaling Handle Position signal

Having pixel position of the handler is not very good - we would much rather have a position that is meaningful to our graph, e.g. a year. 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.

ver código fuente
// add "scales" root value:
  "scales": [
    {
      "name": "yearsScale",
      "type": "linear",
      "zero": false,
      "domain": [1960, 2013],
      "range": "width"
    }
  ],

// in "signals", add:
    {
      "name": "scaledHandlePosition",
      "expr": "handlePosition",
      "scale": {"name": "yearsScale","invert": true}
    }

  // add in "marks"
    {
      "name": "debugScaledHandlePosition",
      "type": "text",
      "properties": {
        "enter": {
          "x": {"value": 250},
          "y": {"value": 28},
          "fill": {"value": "black"}
        },
        "update": {"text": {"signal": "scaledHandlePosition"}}
      }
    }

Year value cleanup

As you might have noticed, the handle could be moved beyond the length of our graph, producing incorrect results. 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:

ver código fuente
    // New signal:
    {
      "name": "currentYear",
      "init": 2000,
      "expr": "clamp(parseInt(scaledHandlePosition),1960,2013)"
    }

    // Update yearLabel mark:
    // New mark to draw the year
    {
      "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"}}
      }
    },

    // Update handle mark:
    {
      "name": "handle",
      "properties": {
        "update": {
          "x": {"scale": "yearsScale","signal": "currentYear"}
        },
      }
    }

Final cleanup

Now we can remove all the debugging marks. We also don't need separate handlePosition and scaledHandlePosition signals because scaling can happen in the same step:

ver código fuente
{
  // 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"} }
      }
    }
  ]
}

Next Step

Please continue to part 2