Skip to content

Instantly share code, notes, and snippets.

@hugolpz
Last active December 7, 2023 17:18
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save hugolpz/824446bb2f9bc8cce607 to your computer and use it in GitHub Desktop.
Save hugolpz/824446bb2f9bc8cce607 to your computer and use it in GitHub Desktop.
.data() vs .datum()

This project aim to illustrate with code the similarities and differences between .data() and .datum(). Feel free to fork and add other parallel examples, where .data() and .datum() do the same work with different syntaxes.

.data() and .datum()'s generated elements are taggued and get collored accordingly using CSS class) :

.data:hover  { fill: #B10000; opacity: 1; } /*  data = RED   */
.datum:hover { fill: #00B100; opacity: 1; } /*  datum= GREEN */

Syntax

Given data such:

var json = [
	{"x":,"y":,"name":"John", "scores": [2,12,7]},
	{"x":,"y":,"name":"Jean", "scores": [4,10,6]},
	{"x":,"y":,"name":"Jack", "scores": [8, 6,2]}
];

The number of elements in your data array should match the number of elements in the selection. To bind your json elements to a single SVG element, wrap in an array:

var chart = d3.select("body").append("svg")
    .data([json])
    .attr("class", "chart")
    …

Or [selection.datum][3] without data-joins:

var chart = d3.select("body").append("svg")
    .datum(json)
    .attr("class", "chart")
    …

to create multiple SVG elements then use data-joins :

var chart = d3.select("body").selectAll("svg")
    .data(json)
  .enter()
  	.append("svg")
    .attr("class", "chart")
    …


var rect = chart.selectAll("rect")
	.data(function(d) { return d.scores; })
  .enter().append("rect")
	…

Simple English

Both are ways to join data.

.datum([values[, key]]) rather for static datavizualisation not needing updates, binds data directly into an element to inject without computing a join.

.data([value]) : Joins the specified array of data with the current selection. The specified value is an array of data values, such as an array of numbers [1,4,3,6] or objects [{ "x":3,"y":2},{ "x":2,"y":5}] or a function that returns an array of values, such function(d) { return d.scores; }.

Binds data-joins and add a generalisation allowing interactive dataviz by providing 3 sets :

  • .enter(), output the set of graphic elements for which no data exists any longer, aka enter
  • .data() (make update), output the set of elements for which the data is updated, aka update
  • .exit(), output the set of elements for which no data item existed before, aka exit
  • .append(), check all the 3 sets above, append the new elements with their values.

To read

Official:

Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
border: 3px solid #646464;
background-color: #C6ECFF;
}
* {
fill-opacity: 0.3;
stroke-opacity: 0.3;
}
.polygon, .lineString, .point {
stroke-width: 1px;
stroke: #0978AB;;
stroke-linejoin: round ;
}
.polygon, .point {
fill:#C6ECFF;
}
.lineString {
fill: none;
}
.L1 { fill: #FFFFFF;}
.data:hover { stroke: #B10000; stroke-width: 2px; fill-opacity: 1; stroke-opacity: 0.7; }
.datum:hover { stroke: #00B100; stroke-width: 2px; fill-opacity: 1; stroke-opacity: 0.7; }
/*.subunit-boundary {
fill: none;
stroke-linejoin: round;
} */
.international-boundary {
stroke-width:3px;
stroke-dasharray: 16,4,3,4;
}
.legend {
font-size: 16px;
font-weight: bold;
text-anchor: start;
}
.download {
background: #333;
color: #FFF;
font-weight: 900;
border: 2px solid #B10000;
padding: 4px;
margin:4px;
}
</style>
<body>
<script src="http://code.jquery.com/jquery-2.0.2.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
// 1. -------------- SETTINGS ------------- //
// India geo-frame borders in decimal ⁰
var WNES = { "W": 67.0, "N":37.5, "E": 99.0, "S": 5.0, "vert_%": 106, "jsond":"India" };
// var WNES = { "W": -5.8, "N":51.5, "E": 10, "S": 41.0, "vert_%": 140, "jsond":"France" };
// Geo values of interest :
var latCenter = (WNES.S + WNES.N)/2,
lonCenter = (WNES.W + WNES.E)/2,
geo_width = (WNES.E - WNES.W),
geo_height= (WNES.N - WNES.S);
// HTML expected jsondme dimensions
var width = 600,
height = width * (geo_height / geo_width);
// var color = d3.scale.category10(); // d3.scale.ordinal().domain(["000000", "FFFFFF", "baz"]).range(colorbrewer.RdBu[9]);
// Projection: projection, reset scale and translate
var projection = d3.geo.equirectangular()
.scale(1)
.translate([0, 0]);
// SVG injection:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("id", "background")
.attr("width", width)
.attr("height", height)
.attr("fill", "#C6ECFF")
.attr("fill-opacity", 0.2);
// Path
var path = d3.geo.path()
.projection(projection)
.pointRadius(4);
// Data (getJSON: TopoJSON)
d3.json("final_adms_India.json", showData);
// .json contain features layers : countries, subunits, places. Each shape have an attibute `name`.
// ---------- FUNCTION ------------- //
function showData(error, jsond) {
// var #Coord: projection formerly here
// var #Path: formerly here
var countries = topojson.feature(jsond, jsond.objects.countries),
subunits = topojson.feature(jsond, jsond.objects.subunits),
places = topojson.feature(jsond, jsond.objects.places),
neighbors = topojson.neighbors(jsond.objects.subunits.geometries); // coloring: full line
// Focus area box compute for derive scale & translate.
// [​[left, bottom], [right, top]​] // E W N S
var b = path.bounds(countries),
s = 1 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
// Projection update
projection = projection
.scale(s)
.translate(t);
var grp1 = svg.append("g")
.attr("class", "polygon")
.on("click", click);
var grp2 = svg.append("g")
.attr("class", "lineString");
var grp3 = svg.append("g")
.attr("class", "point")
.on("click", click);
/** ******************* POLYGONS & .FEATURE() ******************** /**/
//Append L0 polygons
grp1.append("path") //datum
.datum(countries)
.attr("class", "datum L0") // for fun and svg code readability:
.attr("d", path);
grp1.selectAll(".polygons") //data
.data(topojson.feature(jsond, jsond.objects.countries).features)
.enter().append("path")
.attr("class", "data L0") // for fun and svg code readability:
.attr("data-name-en", function(d) { return d.properties.name; }) //for fun and svg code readability
.attr("d", path);
//Append L1 polygons
grp1.append("path")
.datum(topojson.feature(jsond, jsond.objects.subunits))
.attr("class", "datum L1") // for fun and svg code readability
.attr("d", path);
grp1.selectAll(".polygons")
.data(topojson.feature(jsond, jsond.objects.subunits).features)
.enter().append("path")
.attr("class", "data L1") // for fun and svg code readability:
.attr("data-name-en", function(d) { return d.properties.name; }) // for fun and svg code readability:
.attr("d", path);
/** ******************* LINESTRING & .MESH() ******************** /**/
// DATUM : LineString: Conditional Border
grp2.append("path")
.datum(topojson.mesh(jsond, jsond.objects.subunits, function(a,b) { if (a.properties.name!==b.properties.name){var ret = a;}return ret;}))
.attr("class", "datum subunit-boundary")
.attr("d", path);
// DATA > LineSTRING: Conditional Border ? HOW TO DO THIS WITH DATA ()
grp2.append("path")
.datum(topojson.mesh(jsond, jsond.objects.countries, function(a,b) { if (a.properties.name!==b.properties.name){var ret = a;}return ret;}))
.attr("class", "datum international-boundary")
.attr("d", path);
/** ******************* POINTS ******************** /**/
//* DATUM > POINTS:
grp3.append("path")
.datum(topojson.feature(jsond, jsond.objects.places))
.attr("class", "datum points") // for fun and svg code readability
.attr("d", path); /**/
/** ******************* SUGAR ******************** /**/
function click(a){ console.log(a.properties.name);}
// Toggle function here
var leg = svg.append("g")
.attr("groupmode","layer")
.attr("class", "legend")
.attr({'id':'Legend','label':'Legend'});
leg.append("text")
.attr("x", 20)
.attr("y", height - 20)
.style("fill", "#B10000")
.on("click", function(){
console.log('1: click');
// Determine if current line is visible
var newOpacity = data.active ? 1 : 0;
// Hide or show the elements
d3.select(".data").style("opacity", newOpacity);
// Toggle elements's active s
data.active = !data.active;
})
.text(".data()");
leg.append("text")
.attr("x", 20)
.attr("y", height - 40)
.style("fill", "#00B100")
.on("click", function(){
console.log('1: click');
// Determine if current line is visible
var newOpacity = datum.active ? 1 : 0;
// Hide or show the elements
d3.select(".datum").style("opacity", newOpacity);
// Toggle elements's active s
datum.active = !datum.active;
})
.text(".datum()");
}
</script>
<br />
<div>
<a class="download ac-icon-download" href="javascript:javascript: (function () { var e = document.createElement('script'); if (window.location.protocol === 'https:') { e.setAttribute('src', 'https://rawgithub.com/NYTimes/svg-crowbar/gh-pages/svg-crowbar.js'); } else { e.setAttribute('src', 'http://nytimes.github.com/svg-crowbar/svg-crowbar.js'); } e.setAttribute('class', 'svg-crowbar'); document.body.appendChild(e); })();"><!--⤋--><big>⇩</big> Download</a> -- Works on Chrome. Feedback welcome for others web browsers.
</div>
<br />
</body>
</html>
@theredpea
Copy link

Nice reference, Hugo
I think you have these signatures flipped; data accepts a key parameter, datum does not.

.datum([values[, key]])
.data([value]) :

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment