Skip to main content

Representing node attributes in glyphs

Rather than positioning nodes with a force-based layout, let's position them based on the value of two of their attributes - their overall number of followers, and number of followers on #EuroVis2019.

As an alternative to positioning nodes based on their attributes, we could instead position them using a force-based layout, and represent the attributes using a glyph.


{

We import data and construct the network as before



"data": [
{
"name": "data",
"url": "/data/eurovis-twitter/Eurovis2019Network.json"
}
],

"networks": [
{
"name": "network",
"nodes": "data.nodes",
"links": "data.links",
"directed": true,
"source_node": [ "id", "source" ],
"target_node": [ "id", "target" ],

"transform": [
{"type": "metric", "metric": "degree"},
{"type": "filterNodes", "expression": "datum.degree > 250 "}
]
}
],

We construct a force-directed layout as in the first example.


"layouts": [
{
"name": "le-mis-layout",
"network": "network",
"type": "d3-force"
}
],


"vis": [

We use the same visual encoding for links


{
"entries": "network.links",
"layout": "le-mis-layout",
"mark": {
"type": "linkpath",
"start": "source",
"end": "target"
"tooltip": {"field": "screen_name"}
}
},

But the encoding for nodes is more complex


{
"entries": "network.nodes",
"layout": "le-mis-layout",

We define a scale inside this vis block: it can only be accessed from the block or its descendents.


"scales": [
{"name": "x", "type": "linear", "domain": [0, 42884], "range": [0, 100]},
{"name": "y", "type": "linear", "domain": [0, 40], "range": [0, 100]}
],

We render several visual elements for each node.


"vis": [

First, we render a rectangle.


{
"mark": {
"type": "rect",
"width": 100,
"height": 100,
"fill": "white",
"stroke": "grey",
"tooltip": {"expression": "datum.screen_name + '(' + datum.count_followers_in_query + ' followers on Eurovis2019, ' + datum.followers_count + ' followers total)' " }
}
},

Then, we render a circle for every node.

Here, "datum" refers to the item being rendered in this vis block, and "params.entry" refers to the item being rendered in the parent vis block.

This allows us to highlight the circle corresponding to the current node.


{
"entries": "network.nodes",
"mark": {
"x": {"field": "followers_count", "scale": "x"},
"y": {"field": "count_followers_in_query", "scale": "y"},
"type": "circle",
"opacity": {"expression": " (datum.id === params.entry.id) ? 1 : 0.5 "}
"fill": {"expression": " (datum.id === params.entry.id) ? 'red' : 'lightgrey' "}
"tooltip": {"expression": "datum.screen_name + '(' + datum.count_followers_in_query + ' followers on Eurovis2019, ' + datum.followers_count + ' followers total)' " }
}
},

Rather than using an expression to set the fill, we could instead superimpose a point for the entry


/*
{
"entries": "entry", /// just this node
"mark": {
"x": {"field": "followers_count", "scale": "x"},
"y": {"field": "count_followers_in_query", "scale": "y"},
"type": "circle",
"fill": "red",
"tooltip": {"expression": "datum.screen_name + '(' + datum.count_followers_in_query + ' followers on Eurovis2019, ' + datum.followers_count + ' followers total)' " }
}
}
*/


],
]
}