
webapp.js.d3sparql.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of corese-server Show documentation
Show all versions of corese-server Show documentation
Corese is a Semantic Web Factory (triple store and SPARQL endpoint) implementing RDF, RDFS, SPARQL 1.1
Query and Update.
The newest version!
//
// d3sparql.js - utilities for visualizing SPARQL results with the D3 library
//
// Web site: http://github.com/ktym/d3sparql/
// Fork: https://github.com/ErwanDemairy/d3sparql
// Copyright: 2013-2015 (C) Toshiaki Katayama ([email protected])
// License: BSD license (same as D3.js)
// Initial version: 2013-01-28
//
var d3sparql = {
version: "d3sparql.js version 2018-05-04",
debug: false // set to true for showing debug information
}
/*
Execute a SPARQL query and pass the result to a given callback function
Synopsis:
*/
d3sparql.fetch = function(url, callback) {
if (d3sparql.debug) { console.log(url) }
var mime = "application/sparql-results+json"
d3.request(url)
.mimeType(mime)
.response(function(request) {
var json = request.responseText
if (d3sparql.debug) { console.log(json) }
callback(JSON.parse(json))
}).get() ;
// d3.xhr(url, mime, function(request) {
// var json = request.responseText
// if (d3sparql.debug) { console.log(json) }
// callback(JSON.parse(json))
// })
/*
// d3.json sometimes fails to retrieve "application/sparql-results+json" as it is designed for "application/json"
d3.json(url, function(error, json) {
if (d3sparql.debug) { console.log(error) }
if (d3sparql.debug) { console.log(json) }
callback(json)
})
*/
}
d3sparql.query = function(endpoint, sparql, callback) {
var url = endpoint + "?query=" + encodeURIComponent(sparql)
if (d3sparql.debug) { console.log(endpoint) }
d3sparql.fetch(url, callback)
}
/*
Convert sparql-results+json object into a JSON graph in the {"nodes": [], "links": []} form.
Suitable for d3.layout.force(), d3.layout.sankey() etc.
Options:
config = {
"key1": "node1", // SPARQL variable name for node1 (optional; default is the 1st variable)
"key2": "node2", // SPARQL variable name for node2 (optional; default is the 2nd varibale)
"label1": "node1label", // SPARQL variable name for the label of node1 (optional; default is the 3rd variable)
"label2": "node2label", // SPARQL variable name for the label of node2 (optional; default is the 4th variable)
"value1": "node1value", // SPARQL variable name for the value of node1 (optional; default is the 5th variable)
"value2": "node2value" // SPARQL variable name for the value of node2 (optional; default is the 6th variable)
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.forcegraph(json, config)
d3sparql.sankey(json, config)
}
TODO:
Should follow the convention in the miserables.json https://gist.github.com/mbostock/4062045 to contain group for nodes and value for edges.
*/
d3sparql.graph = function(json, config) {
config = config || {}
var head = json.head.vars
var data = json.results.bindings
var opts = {
"key1": config.key1 || head[0] || "key1",
"key2": config.key2 || head[1] || "key2",
"label1": config.label1 || head[2] || false,
"label2": config.label2 || head[3] || false,
"value1": config.value1 || head[4] || false,
"value2": config.value2 || head[5] || false,
}
var graph = {
"nodes": [],
"links": []
}
var check = d3.map()
var index = 0
for (var i = 0; i < data.length; i++) {
var key1 = data[i][opts.key1].value
var key2 = data[i][opts.key2].value
var label1 = opts.label1 ? data[i][opts.label1].value : key1
var label2 = opts.label2 ? data[i][opts.label2].value : key2
var value1 = opts.value1 ? data[i][opts.value1].value : false
var value2 = opts.value2 ? data[i][opts.value2].value : false
if (!check.has(key1)) {
graph.nodes.push({"key": key1, "label": label1, "value": value1})
check.set(key1, index)
index++
}
if (!check.has(key2)) {
graph.nodes.push({"key": key2, "label": label2, "value": value2})
check.set(key2, index)
index++
}
graph.links.push({"source": check.get(key1), "target": check.get(key2)})
}
if (d3sparql.debug) { console.log(JSON.stringify(graph)) }
return graph
}
/*
Convert sparql-results+json object into a JSON tree of {"name": name, "value": size, "children": []} format like in the flare.json file.
Suitable for d3.layout.hierarchy() family
* cluster: d3sparql.dendrogram()
* pack: d3sparql.circlepack()
* partition: d3sparql.sunburst()
* tree: d3sparql.roundtree()
* treemap: d3sparql.treemap(), d3sparql.treemapzoom()
Options:
config = {
"root": "root_name", // SPARQL variable name for root node (optional; default is the 1st variable)
"parent": "parent_name", // SPARQL variable name for parent node (optional; default is the 2nd variable)
"child": "child_name", // SPARQL variable name for child node (ptional; default is the 3rd variable)
"value": "value_name" // SPARQL variable name for numerical value of the child node (optional; default is the 4th variable or "value")
}
Synopsis:
d3sparql.sparql(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.roundtree(json, config)
d3sparql.dendrogram(json, config)
d3sparql.sunburst(json, config)
d3sparql.treemap(json, config)
d3sparql.treemapzoom(json, config)
}
*/
d3sparql.tree = function(json, config) {
config = config || {}
var head = json.head.vars
var data = json.results.bindings
var opts = {
"root": config.root || head[0],
"parent": config.parent || head[1],
"child": config.child || head[2],
"value": config.value || head[3] || "value",
}
var pair = d3.map()
var size = d3.map()
var root = data[0][opts.root].value
var parent = child = children = true
for (var i = 0; i < data.length; i++) {
parent = data[i][opts.parent].value
child = data[i][opts.child].value
if (parent != child) {
if (pair.has(parent)) {
children = pair.get(parent)
children.push(child)
} else {
children = [child]
}
pair.set(parent, children)
if (data[i][opts.value]) {
size.set(child, data[i][opts.value].value)
}
}
}
function traverse(node) {
var list = pair.get(node)
if (list) {
var children = list.map(function(d) { return traverse(d) })
// sum of values of children
var subtotal = d3.sum(children, function(d) { return d.value })
// add a value of parent if exists
var total = d3.sum([subtotal, size.get(node)])
return {"name": node, "children": children, "value": total}
} else {
return {"name": node, "value": size.get(node) || 1}
}
}
var tree = traverse(root)
if (d3sparql.debug) { console.log(JSON.stringify(tree)) }
return tree
}
/*
Rendering sparql-results+json object containing multiple rows into a HTML table
Options:
config = {
"selector": "#result"
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.htmltable(json, config)
}
CSS:
*/
d3sparql.htmltable = function(json, config) {
config = config || {}
var head = json.head.vars
var data = json.results.bindings
var opts = {
"selector": config.selector || null
}
var table = d3sparql.select(opts.selector, "htmltable").append("table").attr("class", "table table-bordered")
var thead = table.append("thead")
var tbody = table.append("tbody")
thead.append("tr")
.selectAll("th")
.data(head)
.enter()
.append("th")
.text(function(col) { return col })
var rows = tbody.selectAll("tr")
.data(data)
.enter()
.append("tr")
var cells = rows.selectAll("td")
.data(function(row) {
return head.map(function(col) {
return row[col] ? row[col].value : ""
})
})
.enter()
.append("td")
.text(function(val) { return val })
// default CSS
table.style({
"margin": "10px"
})
table.selectAll("th").style({
"background": "#eeeeee",
"text-transform": "capitalize",
})
}
/*
Rendering sparql-results+json object containing one row into a HTML table
Options:
config = {
"selector": "#result"
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.htmlhash(json, config)
}
CSS:
*/
d3sparql.htmlhash = function(json, config) {
config = config || {}
var head = json.head.vars
var data = json.results.bindings[0]
var opts = {
"selector": config.selector || null
}
var table = d3sparql.select(opts.selector, "htmlhash").append("table").attr("class", "table table-bordered")
var tbody = table.append("tbody")
var row = tbody.selectAll("tr")
.data(function() {
return head.map(function(col) {
return {"head": col, "data": data[col] ? data[col].value : ""}
})
})
.enter()
.append("tr")
row.append("th")
.text(function(d) { return d.head })
row.append("td")
.text(function(d) { return d.data })
// default CSS
table.style({
"margin": "10px"
})
table.selectAll("th").style({
"background": "#eeeeee",
"text-transform": "capitalize",
})
}
/*
Rendering sparql-results+json object into a bar chart
References:
http://bl.ocks.org/mbostock/3885304
http://bl.ocks.org/mbostock/4403522
Options:
config = {
"label_x": "Prefecture", // label for x-axis (optional; default is same as var_x)
"label_y": "Area", // label for y-axis (optional; default is same as var_y)
"var_x": "pref", // SPARQL variable name for x-axis (optional; default is the 1st variable)
"var_y": "area", // SPARQL variable name for y-axis (optional; default is the 2nd variable)
"width": 850, // canvas width (optional)
"height": 300, // canvas height (optional)
"margin": 40, // canvas margin (optional)
"selector": "#result"
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.barchart(json, config)
}
CSS/SVG:
*/
d3sparql.barchart = function(json, config) {
config = config || {}
var head = json.head.vars
var data = json.results.bindings
var opts = {
"label_x": config.label_x || head[0],
"label_y": config.label_y || head[1],
"var_x": config.var_x || head[0],
"var_y": config.var_y || head[1],
"width": config.width || 750,
"height": config.height || 300,
"margin": config.margin || 80, // TODO: to make use of {top: 10, right: 10, bottom: 80, left: 80}
"selector": config.selector || null,
"custom_extent": config.custom_extent || null
}
var scale_x = d3.scaleBand().range([0, opts.width - opts.margin]).round(0.1);
var scale_y = d3.scaleLinear().range([opts.height - opts.margin, 0]);
var axis_x = d3.axisBottom(scale_x);
var axis_y = d3.axisLeft(scale_y);
scale_x.domain(data.map(function(d) { return d[opts.var_x].value }))
if (config.custom_extent) {
scale_y.domain(config.custom_extent)
} else {
scale_y.domain(d3.extent(data, function (d) {
return parseInt(d[opts.var_y].value)
}))
}
d3.select(opts.selector).selectAll("g").remove();
var svg = d3.select(opts.selector)
.attr("width", "90%" )
.attr("height", "90%" )
.append("g");
var ax = svg.append("g")
.attr("class", "axis x")
.attr("transform", "translate(" + opts.margin + "," + (opts.height - opts.margin) + ")")
.call(axis_x)
var ay = svg.append("g")
.attr("class", "axis y")
.attr("transform", "translate(" + opts.margin + ",0)")
.call(axis_y)
var bar = svg.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("transform", "translate(" + opts.margin + "," + 0 + ")")
.attr("class", "bar")
.attr("x", function(d) { return scale_x(d[opts.var_x].value) })
.attr("width", scale_x.bandwidth())
.attr("y", function(d) { return scale_y(d[opts.var_y].value) })
.attr("stroke", "black")
.attr("stroke-width", "0.1px")
.attr("height", function(d) { return opts.height - scale_y(parseInt(d[opts.var_y].value)) - opts.margin })
.on("click", function(d) {if (d.url) {window.open(d.url.value);}});
/*
.call(function(e) {
e.each(function(d) {
console.log(parseInt(d[opts.var_y].value))
})
})
*/
ax.selectAll("text")
.attr("dy", ".35em")
.attr("x", 10)
.attr("y", 0)
.attr("transform", "rotate(90)")
.style("text-anchor", "start");
ax.append("text")
.attr("class", "label")
.text(opts.label_x)
.style("text-anchor", "middle")
// .attr("transform", "translate(" + (opts.width - opts.margin / 2) + "," + 0 + ")")
.attr("x", 0)
.attr("y", opts.margin / 2);
ay.append("text")
.attr("class", "label")
.text(opts.label_y)
.style("text-anchor", "left")
// .attr("transform", "rotate(-90)")
.attr("x", -opts.margin / 2)
.attr("y", opts.height - opts.margin );
// default CSS/SVG
bar.attrs({
"fill": "steelblue",
})
svg.selectAll(".axis").attrs({
"stroke": "black",
"fill": "none",
"shape-rendering": "crispEdges",
})
svg.selectAll("text").attrs({
"stroke": "none",
"fill": "black",
"font-size": "8pt",
"font-family": "sans-serif",
})
}
/*
Rendering sparql-results+json object into a pie chart
References:
http://bl.ocks.org/mbostock/3887235 Pie chart
http://bl.ocks.org/mbostock/3887193 Donut chart
Options:
config = {
"label": "pref", // SPARQL variable name for slice label (optional; default is the 1st variable)
"size": "area", // SPARQL variable name for slice value (optional; default is the 2nd variable)
"width": 700, // canvas width (optional)
"height": 600, // canvas height (optional)
"margin": 10, // canvas margin (optional)
"hole": 50, // radius size of a center hole (optional; 0 for pie, r > 0 for doughnut)
"selector": "#result"
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.piechart(json, config)
}
CSS/SVG:
*/
d3sparql.piechart = function(json, config) {
config = config || {}
var head = json.head.vars
var data = json.results.bindings
var opts = {
"label": config.label || head[0],
"size": config.size || head[1],
"width": config.width || 700,
"height": config.height || 700,
"margin": config.margin || 10,
"hole": config.hole || 100,
"selector": config.selector || null
}
var radius = Math.min(opts.width, opts.height) / 2 - opts.margin
var hole = Math.max(Math.min(radius - 50, opts.hole), 0)
var color = d3.schemeCategory20
var arc = d3.arc()
.outerRadius(radius)
.innerRadius(hole)
var pie = d3.pie()
//.sort(null)
.value(function(d) { return d[opts.size].value })
// var svg = d3sparql.select(opts.selector, "piechart").append("svg")
d3.select(opts.selector).selectAll("g").remove();
var svg = d3.select(opts.selector)
.attr("width", "90%")
.attr("height", "90%")
.append("g");
// .attr("transform", "translate(" + opts.width / 2 + "," + opts.height / 2 + ")")
var g = svg.selectAll(".arc")
.data(pie(data))
.enter()
.append("g")
.attr("class", "arc")
var slice = g.append("path")
.attr("d", arc)
.attr("fill", function(d, i) { return color[i % 20] })
.on("click", function(d) {if (d.data.url) {window.open(d.data.url.value);}});
var text = g.append("text")
.attr("class", "label")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")" })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.data[opts.label].value })
// default CSS/SVG
slice.attrs({
"stroke": "#ffffff",
})
// TODO: not working?
svg.selectAll("text").attrs({
"stroke": "none",
"fill": "black",
"font-size": "20px",
"font-family": "sans-serif",
})
}
/*
Rendering sparql-results+json object into a scatter plot
References:
http://bl.ocks.org/mbostock/3244058
Options:
config = {
"label_x": "Size", // label for x-axis (optional; default is same as var_x)
"label_y": "Count", // label for y-axis (optional; default is same as var_y)
"var_x": "size", // SPARQL variable name for x-axis values (optional; default is the 1st variable)
"var_y": "count", // SPARQL variable name for y-axis values (optional; default is the 2nd variable)
"var_r": "volume", // SPARQL variable name for radius (optional; default is the 3rd variable)
"min_r": 1, // minimum radius size (optional; default is 1)
"max_r": 20, // maximum radius size (optional; default is 20)
"width": 850, // canvas width (optional)
"height": 300, // canvas height (optional)
"margin_x": 80, // canvas margin x (optional)
"margin_y": 40, // canvas margin y (optional)
"selector": "#result"
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.scatterplot(json, config)
}
CSS/SVG:
*/
d3sparql.scatterplot = function(json, config) {
config = config || {}
var head = json.head.vars
var data = json.results.bindings
var opts = {
"label_x": config.label_x || head[0] || "x",
"label_y": config.label_y || head[1] || "y",
"label_r": config.label_r || head[2] || "r",
"var_x": config.var_x || head[0],
"var_y": config.var_y || head[1],
"var_r": config.var_r || head[2] || 5,
"min_r": config.min_r || 1,
"max_r": config.max_r || 20,
"width": config.width || 850,
"height": config.height || 300,
"margin_x": config.margin_x || 80,
"margin_y": config.margin_y || 40,
"selector": config.selector || null
}
var extent_x = d3.extent(data, function(d) { return parseInt(d[opts.var_x].value) })
var extent_y = d3.extent(data, function(d) { return parseInt(d[opts.var_y].value) })
var extent_r = d3.extent(data, function(d) { return parseInt(d[opts.var_r] ? d[opts.var_r].value : opts.var_r) })
var scale_x = d3.scale.linear().range([opts.margin_x, opts.width - opts.margin_x]).domain(extent_x)
var scale_y = d3.scale.linear().range([opts.height - opts.margin_y, opts.margin_y]).domain(extent_y)
var scale_r = d3.scale.linear().range([opts.min_r, opts.max_r]).domain(extent_r)
var axis_x = d3.svg.axis().scale(scale_x)
var axis_y = d3.svg.axis().scale(scale_y).orient("left")
var svg = d3sparql.select(opts.selector, "scatterplot").append("svg")
.attr("width", opts.width)
.attr("height", opts.height)
var circle = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class", "node")
.attr("cx", function(d) { return scale_x(d[opts.var_x].value) })
.attr("cy", function(d) { return scale_y(d[opts.var_y].value) })
.attr("r", function(d) { return scale_r(d[opts.var_r] ? d[opts.var_r].value : opts.var_r) })
.attr("opacity", 0.5)
.append("title")
.text(function(d) { return d[opts.label_r] ? d[opts.label_r].value : opts.label_r })
var ax = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (opts.height - opts.margin_y) + ")")
.call(axis_x)
var ay = svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + opts.margin_x + ",0)")
.call(axis_y)
ax.append("text")
.attr("class", "label")
.text(opts.label_x)
.style("text-anchor", "middle")
.attr("transform", "translate(" + ((opts.width - opts.margin_x) / 2) + "," + (opts.margin_y - 5) + ")")
ay.append("text")
.attr("class", "label")
.text(opts.label_y)
.style("text-anchor", "middle")
.attr("transform", "rotate(-90)")
.attr("x", 0 - (opts.height / 2))
.attr("y", 0 - (opts.margin_x - 20))
// default CSS/SVG
ax.attr({
"stroke": "black",
"fill": "none",
})
ay.attr({
"stroke": "black",
"fill": "none",
})
// This doesn't work with .append("circle") with .append("title") for tooltip
circle.attr({
"stroke": "gray",
"stroke-width": "1px",
"fill": "lightblue",
"opacity": 0.5,
})
//svg.selectAll(".label").attr({
svg.selectAll("text").attr({
"stroke": "none",
"fill": "black",
"font-size": "8pt",
"font-family": "sans-serif",
})
}
/*
Rendering sparql-results+json object into a force graph
References:
http://bl.ocks.org/mbostock/4062045
Options:
config = {
"radius": 12, // static value or a function to calculate radius of nodes (optional)
"charge": -250, // force between nodes (optional; negative: repulsion, positive: attraction)
"distance": 30, // target distance between linked nodes (optional)
"width": 1000, // canvas width (optional)
"height": 500, // canvas height (optional)
"label": "name", // SPARQL variable name for node labels (optional)
"selector": "#result"
// options for d3sparql.graph() can be added here ...
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.forcegraph(json, config)
}
CSS/SVG:
TODO:
Try other d3.layout.force options.
*/
d3sparql.forcegraph = function(json, config) {
config = config || {}
var graph = (json.head && json.results) ? d3sparql.graph(json, config) : json
var scale = d3.scale.linear()
.domain(d3.extent(graph.nodes, function(d) { return parseFloat(d.value) }))
.range([1, 20])
var opts = {
"radius": config.radius || function(d) { return d.value ? scale(d.value) : 1 + d.label.length },
"charge": config.charge || -500,
"distance": config.distance || 50,
"width": config.width || 1000,
"height": config.height || 750,
"label": config.label || false,
"selector": config.selector || null
}
var svg = d3sparql.select(opts.selector, "forcegraph").append("svg")
.attr("width", opts.width)
.attr("height", opts.height)
var link = svg.selectAll(".link")
.data(graph.links)
.enter()
.append("line")
.attr("class", "link")
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter()
.append("g")
var circle = node.append("circle")
.attr("class", "node")
.attr("r", opts.radius)
var text = node.append("text")
.text(function(d) { return d[opts.label || "label"] })
.attr("class", "node")
var force = d3.layout.force()
.charge(opts.charge)
.linkDistance(opts.distance)
.size([opts.width, opts.height])
.nodes(graph.nodes)
.links(graph.links)
.start()
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x })
.attr("y1", function(d) { return d.source.y })
.attr("x2", function(d) { return d.target.x })
.attr("y2", function(d) { return d.target.y })
text.attr("x", function(d) { return d.x })
.attr("y", function(d) { return d.y })
circle.attr("cx", function(d) { return d.x })
.attr("cy", function(d) { return d.y })
})
node.call(force.drag)
// default CSS/SVG
link.attr({
"stroke": "#999999",
})
circle.attr({
"stroke": "black",
"stroke-width": "1px",
"fill": "lightblue",
"opacity": 1,
})
text.attr({
"font-size": "8px",
"font-family": "sans-serif",
})
}
/*
Rendering sparql-results+json object into a sanky graph
References:
https://github.com/d3/d3-plugins/tree/master/sankey
http://bost.ocks.org/mike/sankey/
Options:
config = {
"width": 1000, // canvas width (optional)
"height": 900, // canvas height (optional)
"margin": 50, // canvas margin (optional)
"selector": "#result"
// options for d3sparql.graph() can be added here ...
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.sankey(json, config)
}
CSS/SVG:
Dependencies:
* sankey.js
* Download from https://github.com/d3/d3-plugins/tree/master/sankey
* Put in the HTML section
*/
d3sparql.sankey = function(json, config) {
config = config || {}
var graph = (json.head && json.results) ? d3sparql.graph(json, config) : json
var opts = {
"width": config.width || 750,
"height": config.height || 1200,
"margin": config.margin || 10,
"selector": config.selector || null
}
var nodes = graph.nodes
var links = graph.links
for (var i = 0; i < links.length; i++) {
links[i].value = 2 // TODO: fix to use values on links
}
var sankey = d3.sankey()
.size([opts.width, opts.height])
.nodeWidth(15)
.nodePadding(10)
.nodes(nodes)
.links(links)
.layout(32)
var path = sankey.link()
var color = d3.scale.category20()
var svg = d3sparql.select(opts.selector, "sankey").append("svg")
.attr("width", opts.width + opts.margin * 2)
.attr("height", opts.height + opts.margin * 2)
.append("g")
.attr("transform", "translate(" + opts.margin + "," + opts.margin + ")")
var link = svg.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d", path)
.attr("stroke-width", function(d) { return Math.max(1, d.dy) })
.sort(function(a, b) { return b.dy - a.dy })
var node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" })
.call(d3.behavior.drag()
.origin(function(d) { return d })
.on("dragstart", function() { this.parentNode.appendChild(this) })
.on("drag", dragmove)
)
node.append("rect")
.attr("width", function(d) { return d.dx })
.attr("height", function(d) { return d.dy })
.attr("fill", function(d) { return color(d.label) })
.attr("opacity", 0.5)
node.append("text")
.attr("x", -6)
.attr("y", function(d) { return d.dy/2 })
.attr("dy", ".35em")
.attr("text-anchor", "end")
.attr("transform", null)
.text(function(d) { return d.label })
.filter(function(d) { return d.x < opts.width / 2 })
.attr("x", 6 + sankey.nodeWidth())
.attr("text-anchor", "start")
// default CSS/SVG
link.attr({
"fill": "none",
"stroke": "grey",
"opacity": 0.5,
})
function dragmove(d) {
d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(opts.height - d.dy, d3.event.y))) + ")")
sankey.relayout()
link.attr("d", path)
}
}
/*
Rendering sparql-results+json object into a round tree
References:
http://bl.ocks.org/4063550 Reingold-Tilford Tree
Options:
config = {
"diameter": 800, // canvas diameter (optional)
"angle": 360, // arc angle (optional; less than 360 for wedge)
"depth": 200, // arc depth (optional; less than diameter/2 - label length to fit)
"radius": 5, // node radius (optional)
"selector": "#result"
// options for d3sparql.tree() can be added here ...
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.roundtree(json, config)
}
CSS/SVG:
*/
d3sparql.roundtree = function(json, config) {
config = config || {}
var tree = (json.head && json.results) ? d3sparql.tree(json, config) : json
var opts = {
"diameter": config.diameter || 800,
"angle": config.angle || 360,
"depth": config.depth || 200,
"radius": config.radius || 5,
"selector": config.selector || null
}
var tree_layout = d3.layout.tree()
.size([opts.angle, opts.depth])
.separation(function(a, b) { return (a.parent === b.parent ? 1 : 2) / a.depth })
var nodes = tree_layout.nodes(tree)
var links = tree_layout.links(nodes)
var diagonal = d3.svg.diagonal.radial()
.projection(function(d) { return [d.y, d.x / 180 * Math.PI] })
var svg = d3sparql.select(opts.selector, "roundtree").append("svg")
.attr("width", opts.diameter)
.attr("height", opts.diameter)
.append("g")
.attr("transform", "translate(" + opts.diameter / 2 + "," + opts.diameter / 2 + ")")
var link = svg.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class", "link")
.attr("d", diagonal)
var node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ") translate(" + d.y + ")" })
var circle = node.append("circle")
.attr("r", opts.radius)
var text = node.append("text")
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end" })
.attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180) translate(-8)" })
.text(function(d) { return d.name })
// default CSS/SVG
link.attr({
"fill": "none",
"stroke": "#cccccc",
"stroke-width": "1.5px",
})
circle.attr({
"fill": "#ffffff",
"stroke": "steelblue",
"stroke-width": "1.5px",
"opacity": 1,
})
text.attr({
"font-size": "10px",
"font-family": "sans-serif",
})
}
/*
Rendering sparql-results+json object into a dendrogram
References:
http://bl.ocks.org/4063570 Cluster Dendrogram
Options:
config = {
"width": 900, // canvas width (optional)
"height": 4500, // canvas height (optional)
"margin": 300, // width margin for labels (optional)
"radius": 5, // radius of node circles (optional)
"selector": "#result"
// options for d3sparql.tree() can be added here ...
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.dendrogram(json, config)
}
CSS/SVG:
*/
d3sparql.dendrogram = function(json, config) {
config = config || {}
var tree = (json.head && json.results) ? d3sparql.tree(json, config) : json
var opts = {
"width": config.width || 800,
"height": config.height || 2000,
"margin": config.margin || 350,
"radius": config.radius || 5,
"selector": config.selector || null
}
var cluster = d3.layout.cluster()
.size([opts.height, opts.width - opts.margin])
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x] })
var svg = d3sparql.select(opts.selector, "dendrogram").append("svg")
.attr("width", opts.width)
.attr("height", opts.height)
.append("g")
.attr("transform", "translate(40,0)")
var nodes = cluster.nodes(tree)
var links = cluster.links(nodes)
var link = svg.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal)
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")" })
var circle = node.append("circle")
.attr("r", opts.radius)
var text = node.append("text")
.attr("dx", function(d) { return (d.parent && d.children) ? -8 : 8 })
.attr("dy", 5)
.style("text-anchor", function(d) { return (d.parent && d.children) ? "end" : "start" })
.text(function(d) { return d.name })
// default CSS/SVG
link.attr({
"fill": "none",
"stroke": "#cccccc",
"stroke-width": "1.5px",
})
circle.attr({
"fill": "#ffffff",
"stroke": "steelblue",
"stroke-width": "1.5px",
"opacity": 1,
})
text.attr({
"font-size": "10px",
"font-family": "sans-serif",
})
}
/*
Rendering sparql-results+json object into a sunburst
References:
http://bl.ocks.org/4348373 Zoomable Sunburst
http://www.jasondavies.com/coffee-wheel/ Coffee Flavour Wheel
Options:
config = {
"width": 1000, // canvas width (optional)
"height": 900, // canvas height (optional)
"margin": 150, // margin for labels (optional)
"selector": "#result"
// options for d3sparql.tree() can be added here ...
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.sunburst(json, config)
}
CSS/SVG:
*/
d3sparql.sunburst = function(json, config) {
config = config || {}
var tree = (json.head && json.results) ? d3sparql.tree(json, config) : json
var opts = {
"width": config.width || 1000,
"height": config.height || 900,
"margin": config.margin || 150,
"selector": config.selector || null
}
var radius = Math.min(opts.width, opts.height) / 2 - opts.margin
var x = d3.scale.linear().range([0, 2 * Math.PI])
var y = d3.scale.sqrt().range([0, radius])
var color = d3.scale.category20()
var svg = d3sparql.select(opts.selector, "sunburst").append("svg")
.attr("width", opts.width)
.attr("height", opts.height)
.append("g")
.attr("transform", "translate(" + opts.width/2 + "," + opts.height/2 + ")");
var arc = d3.svg.arc()
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))) })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))) })
.innerRadius(function(d) { return Math.max(0, y(d.y)) })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)) })
var partition = d3.layout.partition()
.value(function(d) {return d.value})
var nodes = partition.nodes(tree)
var path = svg.selectAll("path")
.data(nodes)
.enter()
.append("path")
.attr("d", arc)
.attr("class", "arc")
.style("fill", function(d) { return color((d.children ? d : d.parent).name) })
.on("click", click)
var text = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.attr("transform", function(d) {
var rotate = x(d.x + d.dx/2) * 180 / Math.PI - 90
return "rotate(" + rotate + ") translate(" + y(d.y) + ")"
})
.attr("dx", ".5em")
.attr("dy", ".35em")
.text(function(d) { return d.name })
.on("click", click)
// default CSS/SVG
path.attr({
"stroke": "#ffffff",
"fill-rule": "evenodd",
})
text.attr({
"font-size": "10px",
"font-family": "sans-serif",
})
function click(d) {
path.transition()
.duration(750)
.attrTween("d", arcTween(d))
text.style("visibility", function (e) {
// required for showing labels just before the transition when zooming back to the upper level
return isParentOf(d, e) ? null : d3.select(this).style("visibility")
})
.transition()
.duration(750)
.attrTween("transform", function(d) {
return function() {
var rotate = x(d.x + d.dx / 2) * 180 / Math.PI - 90
return "rotate(" + rotate + ") translate(" + y(d.y) + ")"
}
})
.each("end", function(e) {
// required for hiding labels just after the transition when zooming down to the lower level
d3.select(this).style("visibility", isParentOf(d, e) ? null : "hidden")
})
}
function maxDepth(d) {
return d.children ? Math.max.apply(Math, d.children.map(maxDepth)) : d.y + d.dy
}
function arcTween(d) {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, maxDepth(d)]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius])
return function(d) {
return function(t) {
x.domain(xd(t))
y.domain(yd(t)).range(yr(t))
return arc(d)
}
}
}
function isParentOf(p, c) {
if (p === c) return true
if (p.children) {
return p.children.some(function(d) {
return isParentOf(d, c)
})
}
return false
}
}
/*
Rendering sparql-results+json object into a circle pack
References:
http://mbostock.github.com/d3/talk/20111116/pack-hierarchy.html Circle Packing
Options:
config = {
"width": 800, // canvas width (optional)
"height": 800, // canvas height (optional)
"diameter": 700, // diamieter of the outer circle (optional)
"selector": "#result"
// options for d3sparql.tree() can be added here ...
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.circlepack(json, config)
}
CSS/SVG:
TODO:
Fix rotation angle for each text to avoid string collision
*/
d3sparql.circlepack = function(json, config) {
config = config || {}
var tree = (json.head && json.results) ? d3sparql.tree(json, config) : json
var opts = {
"width": config.width || 800,
"height": config.height || 800,
"diameter": config.diameter || 700,
"selector": config.selector || null
}
var w = opts.width,
h = opts.height,
r = opts.diameter,
x = d3.scale.linear().range([0, r]),
y = d3.scale.linear().range([0, r])
var pack = d3.layout.pack()
.size([r, r])
.value(function(d) { return d.value })
var node = tree
var nodes = pack.nodes(tree)
var vis = d3sparql.select(opts.selector, "circlepack").append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(" + (w - r) / 2 + "," + (h - r) / 2 + ")")
vis.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("class", function(d) { return d.children ? "parent" : "child" })
.attr("cx", function(d) { return d.x })
.attr("cy", function(d) { return d.y })
.attr("r", function(d) { return d.r })
/*
// CSS: circle { ... }
.attr("fill", function(d) { return d.children ? "#1f77b4" : "#cccccc" })
.attr("fill-opacity", function(d) { return d.children ? ".1" : "1" })
.attr("stroke", function(d) { return d.children ? "steelblue" : "#999999" })
.attr("pointer-events", function(d) { return d.children ? "all" : "none" })
.on("mouseover", function() { d3.select(this).attr("stroke", "#ff7f0e").attr("stroke-width", ".5px") })
.on("mouseout", function() { d3.select(this).attr("stroke", "steelblue").attr("stroke-width", ".5px") })
*/
.on("click", function(d) { return zoom(node === d ? tree : d) })
vis.selectAll("text")
.data(nodes)
.enter()
.append("text")
.attr("class", function(d) { return d.children ? "parent" : "child" })
.attr("x", function(d) { return d.x })
.attr("y", function(d) { return d.y })
// .attr("dy", ".35em")
.style("opacity", function(d) { return d.r > 20 ? 1 : 0 })
.text(function(d) { return d.name })
// rotate to avoid string collision
//.attr("text-anchor", "middle")
.attr("text-anchor", "start")
.transition()
.duration(1000)
.attr("transform", function(d) { return "rotate(-30, " + d.x + ", " + d.y + ")" })
d3.select(window).on("click", function() { zoom(tree) })
function zoom(d, i) {
var k = r / d.r / 2
x.domain([d.x - d.r, d.x + d.r])
y.domain([d.y - d.r, d.y + d.r])
var t = vis.transition()
.duration(d3.event.altKey ? 2000 : 500)
t.selectAll("circle")
.attr("cx", function(d) { return x(d.x) })
.attr("cy", function(d) { return y(d.y) })
.attr("r", function(d) { return k * d.r })
t.selectAll("text")
.attr("x", function(d) { return x(d.x) })
.attr("y", function(d) { return y(d.y) })
.style("opacity", function(d) { return k * d.r > 20 ? 1 : 0 })
d3.event.stopPropagation()
}
}
/*
Rendering sparql-results+json object into a treemap
References:
http://bl.ocks.org/4063582 Treemap
Options:
config = {
"width": 800, // canvas width (optional)
"height": 500, // canvas height (optional)
"margin": {"top": 10, "right": 10, "bottom": 10, "left": 10},
"selector": "#result"
// options for d3sparql.tree() can be added here ...
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.treemap(json, config)
}
CSS/SVG:
*/
d3sparql.treemap = function(json, config) {
config = config || {}
var tree = (json.head && json.results) ? d3sparql.tree(json, config) : json
var opts = {
"width": config.width || 800,
"height": config.height || 500,
"count": config.count || false,
"color": config.color || d3.scale.category20c(),
"margin": config.margin || {top: 0, right: 0, bottom: 0, left: 0},
"selector": config.selector || null
}
var width = opts.width - opts.margin.left - opts.margin.right
var height = opts.height - opts.margin.top - opts.margin.bottom
var color = opts.color
function count(d) { return 1 }
function size(d) { return d.value }
var treemap = d3.layout.treemap()
.size([width, height])
.sticky(true)
.value(opts.count ? count : size)
var div = d3sparql.select(opts.selector, "treemap")
.style("position", "relative")
.style("width", opts.width + "px")
.style("height", opts.height + "px")
.style("left", opts.margin.left + "px")
.style("top", opts.margin.top + "px")
var node = div.datum(tree).selectAll(".node")
.data(treemap.nodes)
.enter()
.append("div")
.attr("class", "node")
.call(position)
.style("background", function(d) { return d.children ? color(d.name) : null })
.text(function(d) { return d.children ? null : d.name })
// default CSS/SVG
node.style({
"border-style": "solid",
"border-width": "1px",
"border-color": "white",
"font-size": "10px",
"font-family": "sans-serif",
"line-height": "12px",
"overflow": "hidden",
"position": "absolute",
"text-indent": "2px",
})
function position() {
this.style("left", function(d) { return d.x + "px" })
.style("top", function(d) { return d.y + "px" })
.style("width", function(d) { return Math.max(0, d.dx - 1) + "px" })
.style("height", function(d) { return Math.max(0, d.dy - 1) + "px" })
}
}
/*
Rendering sparql-results+json object into a zoomable treemap
References:
http://bost.ocks.org/mike/treemap/ Zoomable Treemaps
http://bl.ocks.org/zanarmstrong/76d263bd36f312cb0f9f
Options:
config = {
"width": 800, // canvas width (optional)
"height": 500, // canvas height (optional)
"margin": {"top": 10, "right": 10, "bottom": 10, "left": 10},
"selector": "#result"
// options for d3sparql.tree() can be added here ...
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
var config = { ... }
d3sparql.treemapzoom(json, config)
}
CSS/SVG:
*/
d3sparql.treemapzoom = function(json, config) {
config = config || {}
var tree = (json.head && json.results) ? d3sparql.tree(json, config) : json
var opts = {
"width": config.width || 800,
"height": config.height || 500,
"margin": config.margin || {top: 25, right: 0, bottom: 0, left: 0},
"color": config.color || d3.scale.category20(),
"format": config.format || d3.format(",d"),
"selector": config.selector || null
}
var width = opts.width - opts.margin.left - opts.margin.right
var height = opts.height - opts.margin.top - opts.margin.bottom
var color = opts.color
var format = opts.format
var transitioning
var x = d3.scale.linear().domain([0, width]).range([0, width])
var y = d3.scale.linear().domain([0, height]).range([0, height])
var treemap = d3.layout.treemap()
.children(function(d, depth) { return depth ? null : d.children })
.sort(function(a, b) { return a.value - b.value })
.ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
.round(false)
var svg = d3sparql.select(opts.selector, "treemapzoom").append("svg")
.attr("width", opts.width)
.attr("height", opts.height)
.style("margin-left", -opts.margin.left + "px")
.style("margin.right", -opts.margin.right + "px")
.append("g")
.attr("transform", "translate(" + opts.margin.left + "," + opts.margin.top + ")")
.style("shape-rendering", "crispEdges")
var grandparent = svg.append("g")
.attr("class", "grandparent")
grandparent.append("rect")
.attr("y", -opts.margin.top)
.attr("width", width)
.attr("height", opts.margin.top)
.attr("fill", "#666666")
grandparent.append("text")
.attr("x", 6)
.attr("y", 6 - opts.margin.top)
.attr("dy", ".75em")
.attr("stroke", "#ffffff")
.attr("fill", "#ffffff")
initialize(tree)
layout(tree)
display(tree)
function initialize(tree) {
tree.x = tree.y = 0
tree.dx = width
tree.dy = height
tree.depth = 0
}
// Compute the treemap layout recursively such that each group of siblings
// uses the same size (1×1) rather than the dimensions of the parent cell.
// This optimizes the layout for the current zoom state. Note that a wrapper
// object is created for the parent node for each group of siblings so that
// the parent’s dimensions are not discarded as we recurse. Since each group
// of sibling was laid out in 1×1, we must rescale to fit using absolute
// coordinates. This lets us use a viewport to zoom.
function layout(d) {
if (d.children) {
treemap.nodes({children: d.children})
d.children.forEach(function(c) {
c.x = d.x + c.x * d.dx
c.y = d.y + c.y * d.dy
c.dx *= d.dx
c.dy *= d.dy
c.parent = d
layout(c)
})
}
}
function display(d) {
grandparent
.datum(d.parent)
.on("click", transition)
.select("text")
.text(name(d))
var g1 = svg.insert("g", ".grandparent")
.datum(d)
.attr("class", "depth")
var g = g1.selectAll("g")
.data(d.children)
.enter()
.append("g")
g.filter(function(d) { return d.children })
.classed("children", true)
.on("click", transition)
g.selectAll(".child")
.data(function(d) { return d.children || [d] })
.enter()
.append("rect")
.attr("class", "child")
.call(rect)
g.append("rect")
.attr("class", "parent")
.call(rect)
.append("title")
.text(function(d) { return format(d.value) })
g.append("text")
.attr("dy", ".75em")
.text(function(d) { return d.name })
.call(text)
function transition(d) {
if (transitioning || !d) return
transitioning = true
var g2 = display(d),
t1 = g1.transition().duration(750),
t2 = g2.transition().duration(750)
// Update the domain only after entering new elements.
x.domain([d.x, d.x + d.dx])
y.domain([d.y, d.y + d.dy])
// Enable anti-aliasing during the transition.
svg.style("shape-rendering", null)
// Draw child nodes on top of parent nodes.
svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth })
// Fade-in entering text.
g2.selectAll("text").style("fill-opacity", 0)
// Transition to the new view.
t1.selectAll("text").call(text).style("fill-opacity", 0)
t2.selectAll("text").call(text).style("fill-opacity", 1)
t1.selectAll("rect").call(rect)
t2.selectAll("rect").call(rect)
// Remove the old node when the transition is finished.
t1.remove().each("end", function() {
svg.style("shape-rendering", "crispEdges")
transitioning = false
})
}
return g
}
function text(text) {
text.attr("x", function(d) { return x(d.x) + 6 })
.attr("y", function(d) { return y(d.y) + 6 })
}
function rect(rect) {
rect.attr("x", function(d) { return x(d.x) })
.attr("y", function(d) { return y(d.y) })
.attr("width", function(d) { return x(d.x + d.dx) - x(d.x) })
.attr("height", function(d) { return y(d.y + d.dy) - y(d.y) })
.attr("fill", function(d) { return color(d.name) })
rect.attr({
"stroke": "#ffffff",
"stroke-width": "1px",
"opacity": 0.8,
})
}
function name(d) {
return d.parent
? name(d.parent) + " / " + d.name
: d.name
}
}
/*
World Map spotted by coordinations (longitude and latitude)
Options:
config = {
"var_lat": "lat", // SPARQL variable name for latitude (optional; default is the 1st variable)
"var_lng": "lng", // SPARQL variable name for longitude (optional; default is the 2nd variable)
"width": 960, // canvas width (optional)
"height": 480, // canvas height (optional)
"radius": 5, // circle radius (optional)
"color": "#FF3333, // circle color (optional)
"topojson": "path/to/world-50m.json", // TopoJSON file
"selector": "#result"
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
d3sparql.coordmap(json, config = {})
}
Dependencies:
* topojson.js
* Download from http://d3js.org/topojson.v1.min.js
* Put in the HTML section
* world-50m.json
* Download from https://github.com/mbostock/topojson/blob/master/examples/world-50m.json
*/
d3sparql.coordmap = function(json,config) {
config = config || {}
var head = json.head.vars
var data = json.results.bindings
var opts = {
"var_lat": config.var_lat || head[0] || "lat",
"var_lng": config.var_lng || head[1] || "lng",
"width": config.width || 960,
"height": config.height || 480,
"radius": config.radius || 5,
"color": config.color || "#FF3333",
"topojson": config.topojson || "world-50m.json",
"selector": config.selector || null
}
var projection = d3.geo.equirectangular()
.scale(153)
.translate([opts.width / 2, opts.height / 2])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3sparql.select(opts.selector, "coordmap").append("svg")
.attr("width", opts.width)
.attr("height", opts.height);
svg.append("path")
.datum(graticule.outline)
.attr("fill","#a4bac7")
.attr("d",path);
svg.append("path")
.datum(graticule)
.attr("fill","none")
.attr("stroke","#333333")
.attr("stroke-width",".5px")
.attr("stroke-opacity",".5")
.attr("d", path);
d3.json(opts.topojson, function(error, world) {
svg.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("fill", "#d7c7ad")
.attr("stroke", "#766951")
.attr("d", path);
svg.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b }))
.attr("class", "boundary")
.attr("fill", "none")
.attr("stroke", "#a5967e")
.attr("stroke-width", ".5px")
.attr("d", path);
svg.selectAll(".pin")
.data(data)
.enter().append("circle", ".pin")
.attr("fill",opts.color)
.attr("r", opts.radius)
.attr("stroke","#455346")
.attr("transform", function(d) {
return "translate(" + projection([
d[opts.var_lng].value,
d[opts.var_lat].value
]) + ")"
});
});
}
/*
World Map colored by location names defined in a TopoJSON file
Options:
config = {
"label": "name", // SPARQL variable name for location names (optional; default is the 1st variable)
"value": "size", // SPARQL variable name for numerical values (optional; default is the 2nd variable)
"width": 1000, // canvas width (optional)
"height": 1000, // canvas height (optional)
"color_max": "blue", // color for maximum value (optional)
"color_min": "white", // color for minimum value (optional)
"color_scale": "linear" // color scale (optional; "linear" or "log")
"topojson": "path/to/japan.topojson", // TopoJSON file
"mapname": "japan", // JSON key name of a map location root (e.g., "objects":{"japan":{"type":"GeometryCollection", ...)
"keyname": "name", // JSON key name of map locations matched with "label" (e.g., "properties":{"name":"Tokyo", ...)
"center_lat": 34, // latitude for a map location center (optional; default is 34 for Japan)
"center_lng": 137, // longitude for a map location center (optional; default is 137 for Japan)
"scale": 10000, // scale of rendering (optional)
"selector": "#result"
}
Synopsis:
d3sparql.query(endpoint, sparql, render)
function render(json) {
d3sparql.namedmap(json, config = {})
}
Dependencies:
* topojson.js
* Download from http://d3js.org/topojson.v1.min.js
* Put in the HTML section
* japan.topojson
* Download from https://github.com/sparql-book/sparql-book/blob/master/chapter5/D3/japan.topojson
*/
d3sparql.namedmap = function(json, config) {
config = config || {}
var head = json.head.vars
var data = json.results.bindings
var opts = {
"label": config.label || head[0] || "label",
"value": config.value || head[1] || "value",
"width": config.width || 1000,
"height": config.height || 1000,
"color_max": config.color_max || "red",
"color_min": config.color_min || "white",
"color_scale": config.color_scale || "log",
"topojson": config.topojson || "japan.topojson",
"mapname": config.mapname || "japan",
"keyname": config.keyname || "name_local",
"center_lat": config.center_lat || 34,
"center_lng": config.center_lng || 137,
"scale": config.scale || 10000,
"selector": config.selector || null
}
var size = d3.nest()
.key(function(d) { return d[opts.label].value })
.rollup(function(d) {
return d3.sum(d, function(d) {
return parseInt(d[opts.value].value)
})
}).map(data, d3.map)
var extent = d3.extent((d3.map(size).values()))
if (d3sparql.debug) { console.log(JSON.stringify(size)) }
var svg = d3sparql.select(opts.selector, "namedmap").append("svg")
.attr("width", opts.width)
.attr("height", opts.height)
d3.json(opts.topojson, function(topojson_map) {
var geo = topojson.object(topojson_map, topojson_map.objects[opts.mapname]).geometries
var projection = d3.geo.mercator()
.center([opts.center_lng, opts.center_lat])
.translate([opts.width/2, opts.height/2])
.scale(opts.scale)
var path = d3.geo.path().projection(projection)
switch (opts.color_scale) {
case "log":
var scale = d3.scale.log()
break
default:
var scale = d3.scale.linear()
break
}
var color = scale.domain(extent).range([opts.color_min, opts.color_max])
svg.selectAll("path")
.data(geo)
.enter()
.append("path")
.attr("d", path)
.attr("stroke", "black")
.attr("stroke-width", 0.5)
.style("fill", function(d, i) {
// map SPARQL results to colors
return color(size[d.properties[opts.keyname]])
})
svg.selectAll(".place-label")
.data(geo)
.enter()
.append("text")
.attr("font-size", "8px")
.attr("class", "place-label")
.attr("transform", function(d) {
var lat = d.properties.latitude
var lng = d.properties.longitude
return "translate(" + projection([lng, lat]) + ")"
})
.attr("dx", "-1.5em")
.text(function(d) { return d.properties[opts.keyname] })
})
}
d3sparql.select = function(selector, type) {
if (selector) {
return d3.select(selector).html("").append("div").attr("class", "d3sparql " + type)
} else {
return d3.select("body").append("div").attr("class", "d3sparql " + type)
}
}
/* Helper function only for the d3sparql web site */
d3sparql.toggle = function() {
var button = d3.select("#button")
var elem = d3.select("#sparql")
if (elem.style("display") === "none") {
elem.style("display", "inline")
button.attr("class", "icon-chevron-up")
} else {
elem.style("display", "none")
button.attr("class", "icon-chevron-down")
}
}
/* for IFRAME embed */
d3sparql.frameheight = function(height) {
d3.select(self.frameElement).style("height", height + "px")
}
/* for Node.js */
//module.exports = d3sparql
© 2015 - 2025 Weber Informatics LLC | Privacy Policy