Commit 7257d567 authored by Sylvain Schmitz's avatar Sylvain Schmitz

added chord diagram generator

parent e1a8caf7
<?xml version="1.0"?>
<project name="xpath-benchmark" default="all" basedir="."
xmlns:unless="ant:unless">
<description>Extract data from benchmark results.</description>
<!-- apply XSLT stylesheet -->
<property name="xml" value="../../benchmark/existdb.xml"/>
<target name="all" depends="matrix"/>
<target name="matrix">
<xslt style="matrix.xsl" in="${xml}" out="matrix.json">
<classpath>
<pathelement path="../../lib/saxon.jar"/>
</classpath>
</xslt>
</target>
</project>
\ No newline at end of file
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>Flows Between XPath Fragments</title>
<style>
#circle circle {
fill: none;
pointer-events: all;
}
.group path {
fill-opacity: .5;
}
path.chord {
stroke: #000;
stroke-width: .25px;
}
#circle:hover path.fade {
display: none;
}
</style>
<h1>Outflows in XPathMark</h1>
<p>Links represent XPath entries belonging to one fragment but not the
other. Thicker links represent larger flows. Links are directed;
the colour indicates the most important flow.
<p>Built with <a href="http://d3js.org/">D3</a>.</aside>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://d3js.org/queue.v1.min.js"></script>
<script>
var width = 720,
height = 720,
outerRadius = Math.min(width, height) / 2 - 10,
innerRadius = outerRadius - 24;
//var formatPercent = d3.format(".1%");
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var layout = d3.layout.chord()
.padding(.04)
.sortSubgroups(d3.descending)
.sortChords(d3.ascending);
var path = d3.svg.chord()
.radius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("id", "circle")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.append("circle")
.attr("r", outerRadius);
queue()
.defer(d3.csv, "fragments.csv")
.defer(d3.json, "matrix.json")
.await(ready);
function ready(error, fragments, matrix) {
if (error) throw error;
// number of entries
var nentries = 0;
for (let f of fragments)
nentries += parseInt(f.entries);
layout.matrix = matrix;
layout.n = matrix.length;
layout.padding = .04;
// Redefine the chord layout algorithm
//function relayout() {
var subgroups = {}, groupSums = [], groupIndex = d3.range(layout.n),
subgroupIndex = [], k, x, x0, i, j;
layout.chords = [];
layout.groups = [];
k = 0, i = -1;
while (++i < layout.n) {
x = 0, j = -1;
while (++j < layout.n) {
x += layout.matrix[i][j];
}
groupSums.push(x);
subgroupIndex.push(d3.range(layout.n));
k += x;
}
if (layout.sortGroups) {
groupIndex.sort(function(a, b) {
return layout.sortGroups(groupSums[a], groupSums[b]);
});
}
if (layout.sortSubgroups) {
subgroupIndex.forEach(function(d, i) {
d.sort(function(a, b) {
return layout.sortSubgroups(matrix[i][a], matrix[i][b]);
});
});
}
// angle for one entry
k = (2 * Math.PI - layout.padding * layout.n) / nentries;
x = 0, i = -1;
while (++i < layout.n) {
x0 = x, j = -1;
while (++j < layout.n) {
var di = groupIndex[i], dj = subgroupIndex[di][j], v = layout.matrix[di][dj],
a0 = x, a1 = x += v * k * fragments[di].entries / groupSums[di];
subgroups[di + "-" + dj] = {
index: di,
subindex: dj,
startAngle: a0,
endAngle: a1,
value: v
};
}
layout.groups[di] = {
index: di,
startAngle: x0,
endAngle: x,
value: fragments[di].entries * k //groupSums[di]
};
x += layout.padding;
}
i = -1;
while (++i < layout.n) {
j = i - 1;
while (++j < layout.n) {
var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
if (source.value || target.value) {
layout.chords.push(source.value < target.value ? {
source: target,
target: source
}: {
source: source,
target: target
});
}
}
}
if (layout.sortChords)
layout.chords.sort(function(a, b) {
return layout.sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
});
//}
// Compute the chord layout.
//layout.matrix(matrix);
// Add a group per neighborhood.
var group = svg.selectAll(".group")
.data(layout.groups)
.enter().append("g")
.attr("class", "group")
.on("mouseover", mouseover);
// Add a mouseover title.
group.append("title").text(function(d, i) {
return fragments[i].name + ": " + fragments[i].entries + " entries";
});
// Add the group arc.
var groupPath = group.append("path")
.attr("id", function(d, i) { return "group" + i; })
.attr("d", arc)
.style("fill", function(d, i) { return fragments[i].color; });
// Add a text label.
var groupText = group.append("text")
.attr("x", 6)
.attr("dy", 15);
groupText.append("textPath")
.attr("xlink:href", function(d, i) { return "#group" + i; })
.text(function(d, i) { return fragments[i].name; });
// Remove the labels that don't fit. :(
//groupText.filter(function(d, i) { return groupPath[0][i].getTotalLength() / 2 - 16 < this.getComputedTextLength(); }).remove();
// Add the chords.
var chord = svg.selectAll(".chord")
.data(layout.chords)
.enter().append("path")
.attr("class", "chord")
.style("fill", function(d) { return fragments[d.source.index].color; })
.attr("d", path);
// Add an elaborate mouseover title for each chord.
chord.append("title").text(function(d) {
return "| " + fragments[d.source.index].name
+ " \\ " + fragments[d.target.index].name
+ " | = " + d.source.value
+ "\n| " + fragments[d.target.index].name
+ " \\ " + fragments[d.source.index].name
+ " | = " + d.target.value;
});
function mouseover(d, i) {
chord.classed("fade", function(p) {
return p.source.index != i
&& p.target.index != i;
});
}
}
</script>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text"/>
<xsl:param name="fragments"
select="('xpath-1.0-core.rnc', 'xpath-1.0-downward.rnc', 'xpath-1.0-forward.rnc', 'xpath-1.0-vertical.rnc', 'xpath-1.0-data.rnc', 'xpath-1.0-eval.rnc', 'xpath-1.0.rnc', 'xpath-modal.rnc', 'xpath-hybrid.rnc')"/>
<xsl:template match="/">
<xsl:variable name="root" select="/"/>
<!-- fragments -->
<xsl:result-document method="text" href="fragments.csv">
<xsl:text>name,color,entries
</xsl:text>
<xsl:for-each select="$fragments">
<xsl:call-template name="fragment">
<xsl:with-param name="f" select="string(current())"/>
<xsl:with-param name="root" select="$root"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:result-document>
<!-- matrix -->
<xsl:result-document method="text" href="matrix.json">
<xsl:text>[</xsl:text>
<xsl:for-each select="$fragments">
<xsl:call-template name="line">
<xsl:with-param name="source" select="string(current())"/>
<xsl:with-param name="root" select="$root"/>
</xsl:call-template>
</xsl:for-each>
<xsl:text>]</xsl:text>
</xsl:result-document>
</xsl:template>
<!-- one line of the matrix -->
<xsl:template name="line">
<xsl:param name="source"/>
<xsl:param name="root"/>
<xsl:text>[</xsl:text>
<xsl:for-each select="$fragments">
<xsl:call-template name="entry">
<xsl:with-param name="source" select="$source"/>
<xsl:with-param name="target" select="string(current())"/>
<xsl:with-param name="root" select="$root"/>
</xsl:call-template>
</xsl:for-each>
<xsl:text>]</xsl:text>
<xsl:choose>
<xsl:when test="$source != $fragments[last()]">
<xsl:text>,</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:template>
<!-- one entry of the matrix -->
<xsl:template name="entry">
<xsl:param name="source"/>
<xsl:param name="target"/>
<xsl:param name="root"/>
<xsl:value-of select="count($root//xpath[validation[@schema=$source and @valid='yes'] and validation[@schema=$target and @valid='no']])"/>
<xsl:choose>
<xsl:when test="$target != $fragments[last()]">
<xsl:text>,</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:template>
<!-- one fragment -->
<xsl:template name="fragment">
<xsl:param name="f"/>
<xsl:param name="root"/>
<xsl:variable name="name" select="substring-before(substring-after($f,'xpath-'),'.rnc')"/>
<xsl:choose>
<xsl:when test="matches($name,'1.0-.*')">
<xsl:value-of select="substring-after($name,'1.0-')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$name"/>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="matches($f,'.*-core.rnc')">
<xsl:text>,#ccebc5,</xsl:text>
</xsl:when>
<xsl:when test="matches($f,'.*-(downward|forward|vertical).rnc')">
<xsl:text>,#b3cde3,</xsl:text>
</xsl:when>
<xsl:when test="matches($f,'.*-(data|eval|leashed).rnc')">
<xsl:text>,#fbb4ae,</xsl:text>
</xsl:when>
<xsl:when test="matches($f,'.*-[123].0.rnc')">
<xsl:text>,#decbe4,</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>,#fed9a6,</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="count($root//xpath[validation[@schema=$f and @valid='yes']])"/>
</xsl:template>
</xsl:stylesheet>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment