Skip to content

Instantly share code, notes, and snippets.

@cvlong
Forked from sfsheath/README.md
Created April 30, 2014 20:47
Show Gist options
  • Save cvlong/e676382cd4a617dc14a1 to your computer and use it in GitHub Desktop.
Save cvlong/e676382cd4a617dc14a1 to your computer and use it in GitHub Desktop.

NOTE: GIVE DATA 5+ SECONDS TO LOAD.

The above figure allows relationships between the "year created", "seating capacity", elevation, longitude and latitude of Roman amphitheaters to be explored. The data is incomplete, though it is being regularly expanded and improved.

Drag your mouse along the axes to define ranges of interest. Hover over a blue line to which amphitheater it represents.

The data is compiled on-the-fly by this query.

The following SPARQL should be close to what is used in that link, though I don't guarantee that I've kept it up to date.

PREFIX dc: <http://purl.org/dc/terms/>
PREFIX dbp: <http://dbpedia.org/property/>
PREFIX ex: <http://example.org/>
PREFIX geo: <http://geovocab.org/geometry#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?name ?year_created ?seating_capacity ?elevation ?longitude ?latitude
FROM <http://rdf.greggkellogg.net/distiller?format=rdfxml&in_fmt=jsonld&uri=http://sfsheath.github.io/roman-amphitheaters/roman-amphitheaters.geojson>
WHERE {
?name rdfs:isDefinedBy/dc:created ?year_created .
?name rdfs:isDefinedBy/dbp:seatingCapacity ?seating_capacity .
?name geo:geometry/rdf:value/rdf:first ?longitude .
?name geo:geometry/rdf:value/rdf:rest/rdf:first ?latitude .
?name geo:geometry/rdf:value/rdf:rest/rdf:rest/rdf:first ?elevation .
 }

Further acknowledgements/description:

End result is live querying of a (geo)json-ld data store to make this nifty visualization.

From the original bl.ock: "This parallel coordinates visualization of cars from the ‘70s and ‘80s demonstrates one of D3 2.5.0’s new interactive features: the brush component. By clicking and dragging along any axis, you can specify a filter for that dimension. The brush component is also used in the updated scatterplot matrix example."

<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.background path {
fill: none;
stroke: #ccc;
stroke-opacity: .4;
shape-rendering: crispEdges;
}
.foreground path {
fill: none;
stroke: steelblue;
stroke-width: 2;
stroke-opacity: .7;
}
.brush .extent {
fill-opacity: .3;
stroke: #fff;
shape-rendering: crispEdges;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
text-shadow: 0 1px 0 #fff;
cursor: move;
}
.tooltip {
background-color: rgba(220,220,220,0.5);
color: #333;
margin: 10px;
height: 25px;
padding-right: 10px;
padding-left: 10px;
padding-top: 10px;
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip")
.attr("class","tooltip");
var m = [30, 10, 10, 10],
w = 960 - m[1] - m[3],
h = 500 - m[0] - m[2];
var x = d3.scale.ordinal().rangePoints([0, w], 1),
y = {},
dragging = {};
var line = d3.svg.line(),
axis = d3.svg.axis().orient("left"),
background,
foreground;
var svg = d3.select("body").append("svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
d3.csv("http://sparql.org/sparql?query=PREFIX+dc%3A+%3Chttp%3A%2F%2Fpurl.org%2Fdc%2Fterms%2F%3E%0D%0APREFIX+dbp%3A+%3Chttp%3A%2F%2Fdbpedia.org%2Fproperty%2F%3E%0D%0APREFIX+ex%3A+%3Chttp%3A%2F%2Fexample.org%2F%3E%0D%0APREFIX+geo%3A+%3Chttp%3A%2F%2Fgeovocab.org%2Fgeometry%23%3E%0D%0APREFIX+rdf%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23%3E%0D%0APREFIX+rdfs%3A+%3Chttp%3A%2F%2Fwww.w3.org%2F2000%2F01%2Frdf-schema%23%3E%0D%0ASELECT+%3Fname+%3Fyear_created+%3Fseating_capacity+%3Felevation+%3Flongitude+%3Flatitude%0D%0AFROM+%3Chttp%3A%2F%2Frdf.greggkellogg.net%2Fdistiller%3Fformat%3Drdfxml%26in_fmt%3Djsonld%26uri%3Dhttp%3A%2F%2Fsfsheath.github.io%2Froman-amphitheaters%2Froman-amphitheaters.geojson%3E%0D%0AWHERE+%7B%0D%0A%3Fname+rdfs%3AisDefinedBy%2Fdc%3Acreated+%3Fyear_created+.%0D%0A%3Fname+rdfs%3AisDefinedBy%2Fdbp%3AseatingCapacity+%3Fseating_capacity+.%0D%0A%3Fname+geo%3Ageometry%2Frdf%3Avalue%2Frdf%3Afirst+%3Flongitude+.%0D%0A%3Fname+geo%3Ageometry%2Frdf%3Avalue%2Frdf%3Arest%2Frdf%3Afirst+%3Flatitude+.%0D%0A%3Fname+geo%3Ageometry%2Frdf%3Avalue%2Frdf%3Arest%2Frdf%3Arest%2Frdf%3Afirst+%3Felevation+.%0D%0A+%7D&default-graph-uri=&output=csv&stylesheet=%2Fxml-to-html.xsl", function(error, cars) {
// Extract the list of dimensions and create a scale for each.
x.domain(dimensions = d3.keys(cars[0]).filter(function(d) {
return d != "name" && (y[d] = d3.scale.linear()
.domain(d3.extent(cars, function(p) { return +p[d]; }))
.range([h, 0]));
}));
// Add grey background lines for context.
background = svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(cars)
.enter().append("path")
.attr("d", path);
// Add blue foreground lines for focus.
foreground = svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(cars)
.enter().append("path")
.attr("d", path)
.on("mouseover", function(n){
d3.select(this).transition().duration(100)
.style({'stroke' : '#F00'})
.style({'stroke-width': '4'});
tooltip.text(n.name);
return tooltip.style("visibility", "visible");
})
.on("mousemove", function(){return tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
.on("mouseout", function(d){
d3.select(this).transition().duration(100)
.style({'stroke': 'steelblue' })
.style({'stroke-width' : '2'});
return tooltip.style("visibility", "hidden");
});
// Add a group element for each dimension.
var g = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d) + ")"; })
.call(d3.behavior.drag()
.on("dragstart", function(d) {
dragging[d] = this.__origin__ = x(d);
background.attr("visibility", "hidden");
})
.on("drag", function(d) {
dragging[d] = Math.min(w, Math.max(0, this.__origin__ += d3.event.dx));
foreground.attr("d", path);
dimensions.sort(function(a, b) { return position(a) - position(b); });
x.domain(dimensions);
g.attr("transform", function(d) { return "translate(" + position(d) + ")"; })
})
.on("dragend", function(d) {
delete this.__origin__;
delete dragging[d];
transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
transition(foreground)
.attr("d", path);
background
.attr("d", path)
.transition()
.delay(500)
.duration(0)
.attr("visibility", null);
}));
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(axis.scale(y[d])); })
.append("text")
.attr("text-anchor", "middle")
.attr("y", -9)
.text(String);
// Add and store a brush for each axis.
g.append("g")
.attr("class", "brush")
.each(function(d) { d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brushstart", brushstart).on("brush", brush)); })
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
});
function position(d) {
var v = dragging[d];
return v == null ? x(d) : v;
}
function transition(g) {
return g.transition().duration(500);
}
// Returns the path for a given data point.
function path(d) {
return line(dimensions.map(function(p) { return [position(p), y[p](d[p])]; }));
}
// When brushing, don’t trigger axis dragging.
function brushstart() {
d3.event.sourceEvent.stopPropagation();
}
// Handles a brush event, toggling the display of foreground lines.
function brush() {
var actives = dimensions.filter(function(p) { return !y[p].brush.empty(); }),
extents = actives.map(function(p) { return y[p].brush.extent(); });
foreground.style("display", function(d) {
return actives.every(function(p, i) {
return extents[i][0] <= d[p] && d[p] <= extents[i][1];
}) ? null : "none";
});
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment