Extension:Graph/Interactive Graph Tutorial 2/ja

From Linux Web Expert

Revision as of 20:51, 29 April 2023 by imported>FuzzyBot (Updating to match new version of source page)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)


In this tutorial we will create an interactive graph that will display historical fertility rates per country, with a slider to pick the year, and a map to show rate distribution around the world. The source code for this graph is here. You might also be interested in the complete Vega documentation.

In this tutorial, we continue from the first part of the tutorial.

Drawing a map with some useful data

Now that we built the slider in part 1 of this tutorial, we can add a map and mix it with the highlighting data. We need two sources of data - the map in TopoJSON format is stored in wiki markup here, and the fertility data that is stored as a CSV text in wiki markup, copied from WorldBank (license)

Each country is tagged with a ISO-3 country code. The CSV data contains "country", "id" columns, as well as years 1960..2013, each as a separate column.

<graph mode=interactive title="Historical Fertility Rates"> {

 "version": 2,
 "width": 500,
 "height": 260,
 "padding": 12,
 "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)"
   }
 ],
 "data": [
   {
     "name": "highlights",
     "url": "wikiraw:///Extension:Graph/Demo/RawData:FertilityByCountryHistoric-csv",
     "format": {"type": "csv"},
     "transform": [
       {
         "type": "formula",
         "field": "v",
         "expr": "parseFloat(datum[+currentYear])"
       }
     ]
   },
   {
     "name": "countries",
     "url": "wikiraw:///Extension:Graph/Demo/RawData:WorldMap-iso3-json",
     "format": {"type": "topojson","feature": "countries"},
     "transform": [
       {
         "type": "geopath",
         "value": "data",
         "scale": 80,
         "center": [-180,125],
         "translate": [0,0],
         "projection": "equirectangular"
       },
       {
         "type": "lookup",
         "keys": ["id"],
         "on": "highlights",
         "onKey": "id",
         "as": ["zipped"],
         "default": {"v": null, "country":"No data"}
       }
     ]
   }
 ],
 "scales": [
   {
     "name": "yearsScale",
     "type": "linear",
     "zero": false,
     "domain": [1960,2013],
     "range": "width"
   },
   {
     "name": "color",
     "type": "linear",
     "domain": {"data": "countries","field": "zipped.v"},
     "domainMin": 1,
     "zero": false,
     "range":  ["#FFEDBC", "#f83600"]
   }
 ],
 "marks": [
   {
     "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": "scrollLine",
     "type": "rule",
     "properties": {
       "enter": {
         "x": {"value": 0},
         "y": {"value": 40},
         "x2": {"value": 500},
         "stroke": {"value": "#000"},
         "strokeWidth": {"value": 2}
       }
     }
   },
   {
     "name": "handle",
     "type": "path",
     "properties": {
       "enter": {
         "y": {"value": 40},
         "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"}
       },
       "hover": {"fill": {"value": "#f00"}}
     }
   },
   {
     "name": "map",
     "type": "path",
     "from": {"data": "countries"},
     "properties": {
       "enter": {"path": {"field": "layout_path"}},
       "update": {
         "fill": [
           {
             "test": "datum.zipped.v !== null",
             "scale": "color",
             "field": "zipped.v"
           },
           {"value": "grey"}
         ]
       },
       "hover": {"fill": {"value": "#989898"}}
     }
   }
 ]

} </graph>

  // Increase the size of the graph:
  "width": 500,
  "height": 260,
  // in 'marks', increase the 'x2' for the scrollLine:
  "x2": {"value": 500},

  // add data section
  "data": [
    {
      "name": "highlights",
      "url": "wikiraw:///Extension:Graph/Demo/RawData:FertilityByCountryHistoric-csv",
      "format": {"type": "csv"},
      "transform": [
        {
          "type": "formula",
          "field": "v",
          // convert currentYear to a string and use it as a column name.
          // Parse string value as a float.
          "expr": "parseFloat(datum[''+currentYear])"
        }
      ]
    },
    {
      "name": "countries",
      "url": "wikiraw:///Extension:Graph/Demo/RawData:WorldMap-iso3-json",
      "format": {"type": "topojson","feature": "countries"},
      "transform": [
        {
          // map data needs to be converted into paths for the 'path' mark
          "type": "geopath",
          "value": "data",
          "scale": 80,
          "center": [-180,125],
          "translate": [0,0],
          "projection": "equirectangular"
        },
        {
          // For each country ID in the map, find a matching entry in the highlights table and attach it as 'zipped' value
          "type": "lookup",
          "keys": ["id"],
          "on": "highlights",
          "onKey": "id",
          "as": ["zipped"],
          "default": {"v": null, "country":"No data"}
        }
      ]
    }
  ],
  // add color scale
    {
      "name": "color",
      "type": "linear",
      "domain": {"data": "countries","field": "zipped.v"},
      "domainMin": 1,
      "zero": false,
      "range":  ["#FFEDBC", "#f83600"]
    }
  // add a path drawing mark to show the map
    {
      "name": "map",
      "type": "path",
      "from": {"data": "countries"},
      "properties": {
        "enter": {"path": {"field": "layout_path"}},
        "update": {
          "fill": [
            // fill color should represent the fertility rate from the 'countries' data converted to a color via the 'color' scale., and it should be gray when there is no data.
            // We test if the corresponding value was found, and use it if it was.
            // Tests are like 'if ... else-if ... else ...'
            {
              "test": "datum.zipped.v !== null",
              "scale": "color",
              "field": "zipped.v"
            },
            {"value": "grey"}
          ]
        },
        "hover": {"fill": {"value": "#989898"}}
      }
    }

凡例の追加

Graph will be more informative if the user sees how colors correspond to numbers. Unfortunately the legend placement is a bit broken at the moment and will need to be fixed in Vega, but it tends to work in non-map based graphs.

<graph mode=interactive title="Historical Fertility Rates"> {

 "version": 2,
 "width": 500,
 "height": 260,
 "padding": 12,
 "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)"
   }
 ],
 "data": [
   {
     "name": "highlights",
     "url": "wikiraw:///Extension:Graph/Demo/RawData:FertilityByCountryHistoric-csv",
     "format": {"type": "csv"},
     "transform": [
       {
         "type": "formula",
         "field": "v",
         "expr": "parseFloat(datum[+currentYear])"
       }
     ]
   },
   {
     "name": "countries",
     "url": "wikiraw:///Extension:Graph/Demo/RawData:WorldMap-iso3-json",
     "format": {"type": "topojson","feature": "countries"},
     "transform": [
       {
         "type": "geopath",
         "value": "data",
         "scale": 80,
         "center": [-180,125],
         "translate": [0,0],
         "projection": "equirectangular"
       },
       {
         "type": "lookup",
         "keys": ["id"],
         "on": "highlights",
         "onKey": "id",
         "as": ["zipped"],
         "default": {"v": null, "country":"No data"}
       }
     ]
   }
 ],
 "scales": [
   {
     "name": "yearsScale",
     "type": "linear",
     "zero": false,
     "domain": [1960,2013],
     "range": "width"
   },
   {
     "name": "color",
     "type": "linear",
     "domain": {"data": "countries","field": "zipped.v"},
     "domainMin": 1,
     "zero": false,
     "range":  ["#FFEDBC", "#f83600"]
   }
 ],
 "marks": [
   {
     "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": "scrollLine",
     "type": "rule",
     "properties": {
       "enter": {
         "x": {"value": 0},
         "y": {"value": 40},
         "x2": {"value": 500},
         "stroke": {"value": "#000"},
         "strokeWidth": {"value": 2}
       }
     }
   },
   {
     "name": "handle",
     "type": "path",
     "properties": {
       "enter": {
         "y": {"value": 40},
         "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"}
       },
       "hover": {"fill": {"value": "#f00"}}
     }
   },
   {
     "name": "map",
     "type": "path",
     "from": {"data": "countries"},
     "properties": {
       "enter": {"path": {"field": "layout_path"}},
       "update": {
         "fill": [
           {
             "test": "datum.zipped.v !== null",
             "scale": "color",
             "field": "zipped.v"
           },
           {"value": "grey"}
         ]
       },
       "hover": {"fill": {"value": "#989898"}}
     }
   }
 ],
 "legends": [
   {
     "fill": "color",
     "title": "Fertility",
     "offset": -300,
     "properties": {
       "gradient": {
         "stroke": {"value": "transparent"}
       },
       "title": {
         "fontSize": {"value": 14}
       },
       "legend": {
         "x": {"value": 0},
         "y": {"value": 180}
       }
     }
   }
 ]

} </graph>

  // Add this section to the root object
  "legends": [
    {
      // 'color' is the name of the scale to show
      "fill": "color",
      "title": "Fertility",
      "offset": -300,
      "properties": {
        "gradient": {
          "stroke": {"value": "transparent"}
        },
        "title": {
          "fontSize": {"value": 14}
        },
        "legend": {
          "x": {"value": 0},
          "y": {"value": 180}
        }
      }
    }
  ],

Adding some statistics

The map mouseover event will cause the country and the associated rate show up in the right corner. Note the use of \u007b and \u007d instead of the { and } braces. This is not needed if this graph is inserted directly inside the ‎<graph> tag, but it will prevent issues when graph is used together with the {{ #tag:graph | ...graph definition... | mode=interactive }}.

<graph mode=interactive title="Historical Fertility Rates"> {

 "version": 2,
 "width": 500,
 "height": 260,
 "padding": 12,
 "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)"
   },
   {
     "name": "tooltipSignal",
     "init": {"expr": "{x: 0, y: 0, datum: {} }"}, 
     "streams": [
       {  
         "type": "@map:mouseover",    
         "expr": "{x: eventX(), y: eventY(), datum: eventItem().datum.zipped}"
       },
       {  
         "type": "@map:mouseout",
         "expr": "{x: 0, y: 0, datum: {} }"
       }
     ] 
   }
 ],
 "data": [
   {
     "name": "highlights",
     "url": "wikiraw:///Extension:Graph/Demo/RawData:FertilityByCountryHistoric-csv",
     "format": {"type": "csv"},
     "transform": [
       {
         "type": "formula",
         "field": "v",
         "expr": "parseFloat(datum[+currentYear])"
       }
     ]
   },
   {
     "name": "countries",
     "url": "wikiraw:///Extension:Graph/Demo/RawData:WorldMap-iso3-json",
     "format": {"type": "topojson","feature": "countries"},
     "transform": [
       {
         "type": "geopath",
         "value": "data",
         "scale": 80,
         "center": [-180,125],
         "translate": [0,0],
         "projection": "equirectangular"
       },
       {
         "type": "lookup",
         "keys": ["id"],
         "on": "highlights",
         "onKey": "id",
         "as": ["zipped"],
         "default": {"v": null, "country":"No data"}
       }
     ]
   }
 ],
 "scales": [
   {
     "name": "yearsScale",
     "type": "linear",
     "zero": false,
     "domain": [1960,2013],
     "range": "width"
   },
   {
     "name": "color",
     "type": "linear",
     "domain": {"data": "countries","field": "zipped.v"},
     "domainMin": 1,
     "zero": false,
     "range":  ["#FFEDBC", "#f83600"]
   }
 ],
 "marks": [
   {
     "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": "scrollLine",
     "type": "rule",
     "properties": {
       "enter": {
         "x": {"value": 0},
         "y": {"value": 40},
         "x2": {"value": 500},
         "stroke": {"value": "#000"},
         "strokeWidth": {"value": 2}
       }
     }
   },
   {
     "name": "handle",
     "type": "path",
     "properties": {
       "enter": {
         "y": {"value": 40},
         "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"}
       },
       "hover": {"fill": {"value": "#f00"}}
     }
   },
   {
     "name": "map",
     "type": "path",
     "from": {"data": "countries"},
     "properties": {
       "enter": {"path": {"field": "layout_path"}},
       "update": {
         "fill": [
           {
             "test": "datum.zipped.v !== null",
             "scale": "color",
             "field": "zipped.v"
           },
           {"value": "grey"}
         ]
       },
       "hover": {"fill": {"value": "#989898"}}
     }
   },
   {
     "type": "text",
     "properties": {
       "enter": {
         "x": {"value": 500},
         "y": {"value": 10},
         "align": {"value": "right"},
         "fontSize": {"value": 17},
         "fill": {"value": "black"}
       },
       "update": {
         "text": {"template": "\u007b{tooltipSignal.datum.country}\u007d \u007b{tooltipSignal.datum.v}\u007d"}
       }
     }
   }
 ],
 "legends": [
   {
     "fill": "color",
     "title": "Fertility",
     "offset": -300,
     "properties": {
       "gradient": {
         "stroke": {"value": "transparent"}
       },
       "title": {
         "fontSize": {"value": 14}
       },
       "legend": {
         "x": {"value": 0},
         "y": {"value": 180}
       }
     }
   }
 ]

} </graph>

  // Add a signal that records x and y mouse position and the corresponding data item that was used to draw that specific map element.
  // Note that we don't have to record x and y, but we could have simply used the datum's value.
  // It is kept to show how one may use x,y coordinates to display a floating box with the statistics.
    {
      "name": "tooltipSignal",
      "init": {"expr": "{x: 0, y: 0, datum: {} }"}, 
      "streams": [
        {  
          "type": "@map:mouseover",    
          "expr": "{x: eventX(), y: eventY(), datum: eventItem().datum.zipped}"
        },
        {  
          "type": "@map:mouseout",
          "expr": "{x: 0, y: 0, datum: {} }"
        }
      ] 
    }

    // add this mark to show the value for the specific country's data
    {
      "type": "text",
      "properties": {
        "enter": {
          "x": {"value": 500},
          "y": {"value": 10},  
          "align": {"value": "right"},
          "fontSize": {"value": 17},
          "fill": {"value": "black"}
        },
        "update": {
          // template allows rich value formatting. Templates use {{...}} syntax, which means it conflicts with the Wiki markup, so we replace it with \u007b and \u007d
          "text": {"template": "\u007b{tooltipSignal.datum.country}\u007d \u007b{tooltipSignal.datum.v}\u007d"}
        }
      }
    }