Skip to content

Instantly share code, notes, and snippets.

@renecnielsen
Forked from emeeks/cosit.gexf
Last active August 29, 2015 14:11
Show Gist options
  • Select an option

  • Save renecnielsen/17cf78528ee4fed3ce60 to your computer and use it in GitHub Desktop.

Select an option

Save renecnielsen/17cf78528ee4fed3ce60 to your computer and use it in GitHub Desktop.

Revisions

  1. @emeeks emeeks revised this gist Aug 5, 2014. 3 changed files with 6 additions and 2 deletions.
    5 changes: 5 additions & 0 deletions gexfd3.css
    Original file line number Diff line number Diff line change
    @@ -88,4 +88,9 @@ rect {
    .brush .extent {
    fill-opacity: .90;
    shape-rendering: crispEdges;
    }

    svg {
    width: 100%;
    height:100%;
    }
    1 change: 0 additions & 1 deletion gexfd3.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,3 @@

    gexfD3 =
    function () {

    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -19,7 +19,7 @@
    </div>
    <div id="controls">
    </div>
    <div id="brushDiv"><svg><g id="brushG" class="brush"></g></svg></div>
    <div id="brushDiv"><svg style="width:100%;height:50px;"><g id="brushG" class="brush"></g></svg></div>
    <footer>
    <script>
    nodeFocus = false;
  2. @emeeks emeeks revised this gist May 23, 2014. 1 changed file with 0 additions and 0 deletions.
    Binary file added thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  3. @emeeks emeeks revised this gist Mar 4, 2014. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions gexfd3.js
    Original file line number Diff line number Diff line change
    @@ -42,7 +42,7 @@ gexfD3 =
    newNode.size = gNode.viz.size;
    nodeHash[gNode.id] = newNode;
    for (y in gNode.attributes) {
    if (!isNaN(gNode.attributes[y]) && !(typeof(gNode.attributes[y]) === "undefined")) {
    if (!(typeof(gNode.attributes[y]) === "undefined") && !(gNode.attributes[y].toString() == "NaN" )) {
    newNode.properties[y] = gNode.attributes[y];
    }
    }
    @@ -67,7 +67,8 @@ gexfD3 =
    links.push(newLink)
    x++;
    }

    linkAttributes = d3.keys(links[0].properties);

    sizeExtent = d3.extent(nodes, function(d) {return parseFloat(d.size)})
    sizeScale = d3.scale.linear().domain(sizeExtent).range(nodeScale);
    return this;
  4. @emeeks emeeks revised this gist Mar 4, 2014. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion gexfd3.js
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,4 @@

    gexfD3 =
    function () {

    @@ -41,7 +42,7 @@ gexfD3 =
    newNode.size = gNode.viz.size;
    nodeHash[gNode.id] = newNode;
    for (y in gNode.attributes) {
    if (!Number.isNaN(gNode.attributes[y]) && !(typeof(gNode.attributes[y]) === "undefined")) {
    if (!isNaN(gNode.attributes[y]) && !(typeof(gNode.attributes[y]) === "undefined")) {
    newNode.properties[y] = gNode.attributes[y];
    }
    }
  5. @emeeks emeeks revised this gist Mar 4, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@
    </script>
    <script src="gexfd3.js" type="text/javascript">
    </script>
    <body onload="loadGraph('http://dh.stanford.edu/gexfd3/cosit.gexf')">
    <body onload="loadGraph('cosit.gexf')">

    <div id="vizcontainer" style="width:100%;height:100%">
    <svg id="graphSVG" style="border:1px lightgray solid;">
  6. @emeeks emeeks revised this gist Mar 4, 2014. 1 changed file with 10523 additions and 0 deletions.
    10,523 changes: 10,523 additions & 0 deletions cosit.gexf
    10,523 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  7. @emeeks emeeks revised this gist Mar 4, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -29,7 +29,7 @@
    currentScale = 0;


    function loadGraph(sourceGEXF, linksCSV) {
    function loadGraph(sourceGEXF) {
    newGEXF = GexfParser.fetch(sourceGEXF);

    gD3 = gexfD3().graph(newGEXF).size([1000,1000]).dynamicAttribute("startyr");
  8. @emeeks emeeks revised this gist Mar 4, 2014. 3 changed files with 1 addition and 10525 deletions.
    1 change: 0 additions & 1 deletion gexfd3.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,3 @@

    gexfD3 =
    function () {

    10,523 changes: 0 additions & 10,523 deletions gistfile1.txt
    0 additions, 10,523 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@
    </script>
    <script src="gexfd3.js" type="text/javascript">
    </script>
    <body onload="loadGraph('cosit.gexf')">
    <body onload="loadGraph('http://dh.stanford.edu/gexfd3/cosit.gexf')">

    <div id="vizcontainer" style="width:100%;height:100%">
    <svg id="graphSVG" style="border:1px lightgray solid;">
  9. @emeeks emeeks created this gist Mar 4, 2014.
    91 changes: 91 additions & 0 deletions gexfd3.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,91 @@
    #modal {
    position:fixed;
    left:150px;
    top:20px;
    z-index:1;
    background: white;
    border: 1px black solid;
    box-shadow: 10px 10px 5px #888888;
    display: none;
    }

    #content {
    max-height: 400px;
    overflow: auto;
    }

    #modalClose {
    position: absolute;
    top: -0px;
    right: -0px;
    z-index: 1;
    }
    tr {
    border: 1px gray solid;
    }

    td {
    font-size: 10px;
    }
    td.data {
    font-weight: 900;
    }

    .tick line {
    shape-rendering: crispEdges;
    stroke: #000;
    }

    line.minor {
    stroke: #777;
    stroke-dasharray: 2,2;
    }

    path.domain {
    fill: none;
    stroke: black;
    }

    .inactive, .tentative {
    stroke: darkgray;
    stroke-width: 4px;
    stroke-dasharray: 5 5;
    }

    .tentative {
    opacity: .5;
    }

    .active {
    stroke: black;
    stroke-width: 4px;
    stroke-dasharray: 0;
    }

    circle {
    fill: red;
    }

    rect {
    fill: darkgray;
    }

    #controls {
    position: fixed;
    bottom: 50px;
    left: 20px;
    }
    #brushDiv {
    position: fixed;
    bottom: 100px;
    left: 20px;
    right: 20px;
    height:50px;
    background: white;
    opacity: .75;
    }

    .brush .extent {
    fill-opacity: .90;
    shape-rendering: crispEdges;
    }
    188 changes: 188 additions & 0 deletions gexfd3.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,188 @@

    gexfD3 =
    function () {

    var nodes = [];
    var links = [];
    var linksFile = "";
    var fileName = "";
    var xExtent = [];
    var yExtent = [];
    var nodeScale = [1,10];
    var layoutSize = [500,500];
    var sizeExtent = [];
    var dAtt = "";
    var dynamicExtent = [];
    var sizeScale, xScale, yScale, dynamicScale;
    var gexfD3Brush = d3.svg.brush();
    var linkAttributes = [];
    var nodeAttributes = [];
    var nodeHash = {};

    this.graph = function(gexfParsed) {
    if (!arguments.length) return true;

    var gNodes = gexfParsed.nodes;
    var gLinks = gexfParsed.edges;

    nodes = [];
    links = [];
    nodeHash = {};
    //Create JSON nodes array
    var x = 0;
    gNodes.forEach(function(gNode) {
    var newNode = {id: x, properties: {}};
    newNode.label = gNode.label || gNode.id;
    newNode.rgbColor = gNode.viz.color || "rgb(122,122,122)";
    newNode.x = gNode.viz.position.x;
    newNode.y = gNode.viz.position.y;
    newNode.z = gNode.viz.position.z;
    newNode.originalX = newNode.x;
    newNode.originalY = newNode.y;
    newNode.size = gNode.viz.size;
    nodeHash[gNode.id] = newNode;
    for (y in gNode.attributes) {
    if (!Number.isNaN(gNode.attributes[y]) && !(typeof(gNode.attributes[y]) === "undefined")) {
    newNode.properties[y] = gNode.attributes[y];
    }
    }
    nodes.push(newNode);
    x++;
    })
    //get node attributes based on attributes in the first node
    //this won't work for assymetrical node attributes
    nodeAttributes = d3.keys(nodes[0].properties);

    //Create JSON links array
    var x = 0;
    while (x < gLinks.length) {
    var newLink = {id: x, properties: {}};
    newLink.source = nodeHash[gLinks[x].source];
    newLink.target = nodeHash[gLinks[x].target];
    //process attributes
    for (y in gLinks[x].attributes) {
    newLink.properties[y] = gLinks[x].attributes[y];
    y++;
    }
    links.push(newLink)
    x++;
    }

    sizeExtent = d3.extent(nodes, function(d) {return parseFloat(d.size)})
    sizeScale = d3.scale.linear().domain(sizeExtent).range(nodeScale);
    return this;
    }
    this.nodes = function(incNodes) {
    if (!arguments.length) return nodes;
    nodes = incNodes;
    return this;
    }
    this.links = function(incLinks) {
    if (!arguments.length) return links;
    links = incLinks
    return this;
    }

    this.linkAttributes = function(incAtts) {
    if (!arguments.length) return linkAttributes;
    linkAttributes = incAtts;
    return this;
    }

    this.nodeAttributes = function(incAtts) {
    if (!arguments.length) return nodeAttributes;
    nodeAttributes = incAtts;
    return this;
    }

    this.nodeScale = function(incScale) {
    if (!arguments.length) return sizeScale;
    nodeScale = incScale;
    sizeScale = d3.scale.linear().domain(sizeExtent).range(nodeScale);
    return this;
    }



    this.overwriteLinks = function(incLinks) {
    if (!arguments.length) return nodes;
    data = incLinks;
    //OVERWRITE links for parallel links
    links = [];
    for (x in data) {
    var newLink = {id: x, properties: {}};
    newLink.source = nodeHash[data[x].source];
    newLink.target = nodeHash[data[x].target];
    newLink.id = x;
    newLink.properties.type = "base";
    newLink.properties.year = data[x].year;
    //process attributes
    if (newLink.source && newLink.target) {
    links.push(newLink);
    }
    x++;
    }
    linkAttributes = d3.keys(links[0].properties);

    return this;
    }

    this.size = function(incSize) {
    if (!arguments.length) return layoutSize;

    //Measure
    layoutSize = incSize;
    xExtent = d3.extent(nodes, function(d) {return parseFloat(d.x)})
    yExtent = d3.extent(nodes, function(d) {return parseFloat(d.y)})
    xScale = d3.scale.linear().domain(xExtent).range([0,layoutSize[0]]);
    yScale = d3.scale.linear().domain(yExtent).range([layoutSize[1],0]);
    return this;
    }

    this.dynamicAttribute = function(incAtt) {
    if (!arguments.length) return dAtt;
    dAtt = incAtt;
    var nDE = [Infinity, -Infinity];
    var lDE = [Infinity, -Infinity];
    if (nodeAttributes.indexOf(dAtt) > -1) {
    //currently filters out 0 entries
    // nDE = d3.extent(nodes, function(d) {return parseInt(d.properties[dAtt])})
    nDE = d3.extent(nodes.filter(function(p) {return p.properties[dAtt] != 0}), function(d) {return parseInt(d.properties[dAtt])})
    }
    if (linkAttributes.indexOf(dAtt) > -1) {
    // lDE = d3.extent(links, function(d) {return parseInt(d.properties[dAtt])})
    lDE = d3.extent(links.filter(function(p) {return p.properties[dAtt] != 0}), function(d) {return parseInt(d.properties[dAtt])})
    }
    dynamicExtent = [Math.min(nDE[0],lDE[0]), Math.max(nDE[1],lDE[1])]
    dynamicScale = d3.scale.linear().domain(dynamicExtent).range([0,layoutSize[0]]);
    return this;
    }

    this.dynamicBrush = function(incSelection) {
    if (!arguments.length) return gexfD3Brush;
    gexfD3Brush
    .x(dynamicScale)
    .extent(dynamicExtent)
    var brushAxis = d3.svg.axis().scale(dynamicScale).orient("bottom").tickSize(-40).ticks(20);

    incSelection.append("g").attr("id", "bgAxis").append("g").attr("transform", "translate(50,35)").call(brushAxis)
    incSelection.append("g").attr("id", "fgBrush").attr("transform", "translate(50,0)")
    .call(gexfD3Brush)
    .selectAll("rect").attr("height", 35);
    return this;
    }

    this.xScale = function(newScale) {
    if (!arguments.length) return xScale;
    xScale = newScale;
    return this;
    }

    this.yScale = function(newScale) {
    if (!arguments.length) return yScale;
    yScale = newScale;
    return this;
    }

    return this;
    }
    10,523 changes: 10,523 additions & 0 deletions gistfile1.txt
    10,523 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
    386 changes: 386 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,386 @@
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>GEXF D3 w/ Dynamic Brushing and Reprojecting</title>
    <meta charset="utf-8" />
    <link type="text/css" rel="stylesheet" href="gexfd3.css" />
    </head>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="http://d3js.org/colorbrewer.v1.min.js"></script>
    <script src="parser.js" type="text/javascript">
    </script>
    <script src="gexfd3.js" type="text/javascript">
    </script>
    <body onload="loadGraph('cosit.gexf')">

    <div id="vizcontainer" style="width:100%;height:100%">
    <svg id="graphSVG" style="border:1px lightgray solid;">
    <g id="graphG" />
    <div id="modal"><div id="content"></div><button id="modalClose" onclick="nodeFocus=false;nodeOut();d3.select('#modal').style('display','none');">X</button></div>
    </div>
    <div id="controls">
    </div>
    <div id="brushDiv"><svg><g id="brushG" class="brush"></g></svg></div>
    <footer>
    <script>
    nodeFocus = false;
    currentBrush =[0,0];
    docHash = {};
    allLinks = [];
    currentScale = 0;


    function loadGraph(sourceGEXF, linksCSV) {
    newGEXF = GexfParser.fetch(sourceGEXF);

    gD3 = gexfD3().graph(newGEXF).size([1000,1000]).dynamicAttribute("startyr");

    force = d3.layout.force()
    .charge(-500)
    .linkDistance(150)
    .size([1000, 1000])
    .gravity(.1)
    .on("tick", redrawGraph)

    d3.select("#brushG").call(gD3.dynamicBrush);
    gD3.dynamicBrush().on("brush", brushMove);

    zoom = d3.behavior.zoom()
    .scaleExtent([.1, 10])
    .on("zoom", zoomed);

    allLinks = gD3.links();
    brushMove();

    d3.select("svg").call(zoom);
    createControls();
    zoomed();

    }

    function highlightNeighbors(d,i) {
    var nodeNeighbors = findNeighbors(d,i);
    d3.selectAll("g.node").each(function(p) {
    var isNeighbor = nodeNeighbors.nodes.indexOf(p);
    d3.select(this).select("circle")
    .style("opacity", isNeighbor > -1 ? 1 : .25)
    .style("stroke-width", isNeighbor > -1 ? 3 : 1)
    .style("stroke", isNeighbor > -1 ? "blue" : "white")
    })

    d3.selectAll("line.link")
    .style("stroke-width", function (d) {return nodeNeighbors.links.indexOf(d) > -1 ? 2 : 1})
    .style("opacity", function (d) {return nodeNeighbors.links.indexOf(d) > -1 ? 1 : .25})
    }

    function findNeighbors(d,i) {
    neighborArray = [d];
    var linkArray = [];
    var linksArray = d3.selectAll("line.link").filter(function(p) {return p.source == d || p.target == d}).each(function(p) {
    neighborArray.indexOf(p.source) == -1 ? neighborArray.push(p.source) : null;
    neighborArray.indexOf(p.target) == -1 ? neighborArray.push(p.target) : null;
    linkArray.push(p);
    })
    // neighborArray = d3.set(neighborArray).keys();
    return {nodes: neighborArray, links: linkArray};
    }

    function zoomed() {
    force.stop();
    var canvWidth = parseInt(d3.select("#vizcontainer").style("width"));
    var canvHeight = parseInt(d3.select("#vizcontainer").style("height"));
    if (currentScale != zoom.scale()) {
    currentScale = zoom.scale();
    var halfCanvas = canvHeight / 2;
    var zoomLevel = halfCanvas * currentScale;
    gD3.xScale().range([halfCanvas - zoomLevel, halfCanvas + zoomLevel]);
    gD3.yScale().range([halfCanvas + zoomLevel, halfCanvas - zoomLevel]);
    redrawGraph();
    }
    var canvasTranslate = zoom.translate();
    d3.select("#graphG").attr("transform", "translate("+canvasTranslate[0]+","+canvasTranslate[1]+")")
    }

    function createControls() {
    d3.select("#controls").append("button").attr("class", "origButton").html("Force On").on("click", function() {
    force.start();})
    d3.select("#controls").append("button").attr("class", "origButton").html("Force Off").on("click", function() {
    force.stop();})
    d3.select("#controls").append("button").attr("class", "origButton").html("Reset Layout").on("click", function() {
    force.stop();
    gD3.nodes().forEach(function (el) {el.x = el.originalX;el.px = el.originalX;el.y = el.originalY;el.py = el.originalY;});
    currentBrush = [0,0];
    brushMove();
    redrawGraph();
    })

    d3.select("#controls").append("button").attr("class", "origButton").html("Reset Colors").on("click", function() {
    var sizeScale = gD3.nodeScale();
    d3.selectAll("circle")
    .attr("r", function (d) {return sizeScale(d.size)})
    .style("fill", function(d) {return d.rgbColor})
    .style("opacity", 1);
    d3.selectAll("line.link").style("stroke", "black");
    })
    d3.select("#controls").append("button").attr("class", "origButton").html("N-Partite").on("click", function() {
    collapseNetwork("base");
    })
    d3.select("#controls").selectAll("button.networkProjectionButton").data(
    d3.set(gD3.nodes().map(function(el) {return el.properties.type})).values()
    )
    .enter()
    .append("button")
    .attr("class", "networkProjectionButton")
    .on("click", function(d) {collapseNetwork(d)})
    .html(function(d) {return d + " Only"});

    d3.select("#controls").selectAll("button.nodeButtons").data(gD3.nodeAttributes())
    .enter()
    .append("button")
    .attr("class", "nodeButtons")
    .on("click", nodeButtonClick)
    .html(function(d) {return d});

    d3.select("#controls").selectAll("button.linkButtons").data(gD3.linkAttributes())
    .enter()
    .append("button")
    .attr("class", "linkButtons")
    .on("click", linkButtonClick)
    .html(function(d) {return d});

    }

    function nodeButtonClick(d,i) {
    var nodeAttExtent = d3.extent(filteredNodes, function(p) {return parseFloat(p.properties[d])});
    var colorScale = d3.scale.quantize().domain(nodeAttExtent).range(colorbrewer.YlGnBu[6]);
    d3.selectAll("circle").style("fill", function(p) {return colorScale(p.properties[d])}).style("opacity", 1)
    }
    function linkButtonClick(d,i) {
    var linkAttExtent = d3.extent(filteredLinks, function(p) {return parseFloat(p.properties[d])});
    var colorScale = d3.scale.quantize().domain(linkAttExtent).range(colorbrewer.YlGnBu[6]);
    d3.selectAll("line").style("stroke", function(p) {return colorScale(p.properties[d])}).style("opacity", 1)
    }

    function redrawGraph() {
    var xScale = gD3.xScale();
    var yScale = gD3.yScale();

    d3.selectAll("line.link")
    .attr("x1", function (d) {return xScale(d.source.x)})
    .attr("x2", function (d) {return xScale(d.target.x)})
    .attr("y1", function (d) {return yScale(d.source.y)})
    .attr("y2", function (d) {return yScale(d.target.y)});

    d3.selectAll("g.node")
    .attr("transform", function(d) {return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"});
    }

    function collapseNetwork(collapseVector) {
    currentBrush = [0,0];
    dAtt = gD3.dynamicAttribute();
    if (collapseVector == "base") {
    gD3.links(allLinks);
    brushMove();
    return;
    }
    newLinks = [];
    for (x in allLinks) {
    if (allLinks[x].source.properties.type == collapseVector) {
    for (y in allLinks) {
    if (allLinks[y].source.properties.type == collapseVector) {
    if (allLinks[y].target == allLinks[x].target && (allLinks[y].properties[dAtt] || allLinks[y].source.properties[dAtt]) == (allLinks[x].properties[dAtt] || allLinks[x].source.properties[dAtt])) {
    var newLink = {id: collapseVector + newLinks.length, source: allLinks[x].source, target: allLinks[y].source, properties: {}};
    if (gD3.linkAttributes().indexOf(dAtt) > -1) {
    newLink.properties[dAtt] = allLinks[x].properties[dAtt];
    }
    else if (gD3.nodeAttributes().indexOf(dAtt) > -1) {
    newLink.properties[dAtt] = allLinks[y].target.properties[dAtt];
    }
    newLinks.push(newLink);
    }
    }
    }
    }
    else if (allLinks[x].target.properties.type == collapseVector) {
    for (y in allLinks) {
    if (allLinks[y].target.properties.type == collapseVector) {
    if (allLinks[y].source == allLinks[x].source && (allLinks[y].properties[dAtt] || allLinks[y].target.properties[dAtt]) == (allLinks[x].properties[dAtt] || allLinks[x].target.properties[dAtt])) {
    var newLink = {id: collapseVector + newLinks.length, source: allLinks[x].target, target: allLinks[y].target, properties: {}};
    if (gD3.linkAttributes().indexOf(dAtt) > -1) {
    newLink.properties[dAtt] = allLinks[x].properties[dAtt];
    }
    else if (gD3.nodeAttributes().indexOf(dAtt) > -1) {
    newLink.properties[dAtt] = allLinks[y].source.properties[dAtt];
    }
    newLinks.push(newLink);
    }
    }
    }
    }
    }
    gD3.links(newLinks);
    brushMove();
    }

    function brushMove() {
    var s = gD3.dynamicBrush().extent();
    var dAtt = gD3.dynamicAttribute();
    var xScale = gD3.xScale();
    var yScale = gD3.yScale();
    var sizeScale = gD3.nodeScale();

    if (Math.ceil(s[0]) == currentBrush[0] && Math.floor(s[1]) == currentBrush[1]) {
    return;
    }
    else {
    currentBrush[0] = Math.floor(s[0]);
    currentBrush[1] = Math.ceil(s[1]);
    }

    var forceRunning = false;
    if (force.alpha() > 0) {
    force.stop();
    forceRunning = true;
    }


    if (typeof gD3.links()[0].properties["startyr"] != "undefined") {
    filteredLinks = gD3.links().filter(function (d) {return d.properties[dAtt] == 0 || (d.properties[dAtt] >= currentBrush[0] && d.properties[dAtt] <= currentBrush[1])});
    sourceNodes = filteredLinks.map(function (el) {return el.source});
    targetNodes = filteredLinks.map(function (el) {return el.target});
    filteredNodes = gD3.nodes().filter(function (d) {return sourceNodes.indexOf(d) > -1 || targetNodes.indexOf(d) > -1});
    }
    else {
    filteredLinks = gD3.links();
    filteredNodes = gD3.nodes();
    }
    if (gD3.nodeAttributes().indexOf(dAtt) > -1) {
    filteredNodes = filteredNodes.filter(function (d) {return d.properties[dAtt] == 0 || (d.properties[dAtt] >= currentBrush[0] && d.properties[dAtt] <= currentBrush[1])});
    nodeIDs = filteredNodes.map(function (el) {return el.id})
    filteredLinks = filteredLinks.filter(function (d) {return nodeIDs.indexOf(d.source.id) > -1 && nodeIDs.indexOf(d.target.id) > -1})
    }

    d3.select("#graphG").selectAll("line.link")
    .data(filteredLinks, function (d) {return d.id})
    .enter()
    .insert("line", "g.node")
    .attr("class","link")
    .attr("x1", function (d) {return xScale(d.source.x)})
    .attr("x2", function (d) {return xScale(d.target.x)})
    .attr("y1", function (d) {return yScale(d.source.y)})
    .attr("y2", function (d) {return yScale(d.target.y)})
    .style("stroke", "black")
    .style("stroke-width", "1px")
    .style("opacity", .25)

    d3.select("#graphG").selectAll("g.node").data(filteredNodes, function (d) {return d.id})
    .enter()
    .append("g")
    .attr("class", "node")
    .attr("transform", function(d) {return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"})
    .on("mouseover", nodeOver)
    .on("mouseout", nodeOut)
    .on("click", nodeClick)
    .append("circle")
    .attr("r", 5)
    .style("fill", function(d) {return d.rgbColor})
    .style("stroke", "black")
    .style("stroke-width", "1px")
    .style("stroke-opacity", 1);

    d3.selectAll("g.node").data(filteredNodes, function (d) {return d.id})
    .exit()
    .remove();

    d3.selectAll("line.link").data(filteredLinks, function (d) {return d.id})
    .exit()
    .remove();

    force
    .nodes(filteredNodes)
    .links(filteredLinks);

    force.start();

    var maxWeight = d3.max(filteredNodes, function(d) {return d.weight || 0});

    if (maxWeight > 0) {
    var nodeScale = d3.scale.linear().domain([1,maxWeight]).range([2,10]).clamp(true);
    d3.selectAll("g.node").select("circle").attr("r", function(d) {return nodeScale(d.weight) || 0});
    }

    if (!forceRunning) {
    force.stop();
    }

    function nodeOver(d,i,e) {
    var el = this;
    if (!d3.event.fromElement) {
    el = e;
    }
    if (nodeFocus) {
    return;
    }
    //Only do the element stuff if this came from mouseover
    el.parentNode.appendChild(el);
    d3.select(el).append("text").attr("class", "hoverLabel").attr("stroke", "white").attr("stroke-width", "5px")
    .style("opacity", .9)
    .style("pointer-events", "none")
    .text(d.label);

    d3.select(el).append("text").attr("class", "hoverLabel")
    .style("pointer-events", "none")
    .text(d.label);
    highlightNeighbors(d,i);
    }

    function nodeClick(d,i) {
    nodeFocus = false;
    nodeOut();
    nodeOver(d,i,this);
    nodeFocus = true;
    var newContent = "<p>" + d.label + "</p>";
    newContent += "<p>Attributes: </p><p><ul>";
    for (x in gD3.nodeAttributes()) {
    newContent += "<li>" + gD3.nodeAttributes()[x] + ": " + d.properties[gD3.nodeAttributes()[x]]+ "</li>";
    }
    newContent += "</ul></p><p>Connections:</p><ul>";
    var neighbors = findNeighbors(d,i);
    for (x in neighbors.nodes) {
    if (neighbors.nodes[x] != d) {
    newContent += "<li>" + neighbors.nodes[x].label + "</li>";
    }
    }
    newContent += "</ul></p>";

    d3.select("#modal").style("display", "block").select("#content").html(newContent);
    }

    }


    function createAnonymousEdgeTable() {
    var htmlTable = "source,target,year<br>";
    for (x in gD3.links()) {
    htmlTable+=gD3.links()[x].source.properties.type + "-" + gD3.nodes().indexOf(gD3.links()[x].source) + "," + gD3.links()[x].target.properties.type + "-" + gD3.nodes().indexOf(gD3.links()[x].target) + "," + gD3.links()[x].properties.year;
    htmlTable+="<br>";
    }
    d3.select("#content").html(htmlTable)


    }

    function nodeOut() {
    if (nodeFocus) {
    return;
    }

    d3.selectAll(".hoverLabel").remove();
    d3.selectAll("circle").style("opacity", 1).style("stroke", "black").style("stroke-width", "1px");
    d3.selectAll("line").style("opacity", .25);
    }


    </script>
    </footer>
    </body>
    </html>
    503 changes: 503 additions & 0 deletions parser.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,503 @@
    ;(function(undefined) {
    'use strict';

    /**
    * GEXF Parser
    * ============
    *
    * Author: PLIQUE Guillaume (Yomguithereal)
    * URL: https://github.com/Yomguithereal/gexf-parser
    * Version: 1.0
    */

    /**
    * Helper Namespace
    * -----------------
    *
    * A useful batch of function dealing with DOM operations and types.
    */
    var _helpers = {
    nodeListToArray: function(nodeList) {

    // Return array
    var children = [];

    // Iterating
    for (var i = 0, len = nodeList.length; i < len; ++i) {
    if (nodeList[i].nodeName !== '#text')
    children.push(nodeList[i]);
    }

    return children;
    },
    nodeListEach: function(nodeList, func) {

    // Iterating
    for (var i = 0, len = nodeList.length; i < len; ++i) {
    if (nodeList[i].nodeName !== '#text')
    func(nodeList[i]);
    }
    },
    nodeListToHash: function(nodeList, filter) {

    // Return object
    var children = {};

    // Iterating
    for (var i = 0; i < nodeList.length; i++) {
    if (nodeList[i].nodeName !== '#text') {
    var prop = filter(nodeList[i]);
    children[prop.key] = prop.value;
    }
    }

    return children;
    },
    namedNodeMapToObject: function(nodeMap) {

    // Return object
    var attributes = {};

    // Iterating
    for (var i = 0; i < nodeMap.length; i++) {
    attributes[nodeMap[i].name] = nodeMap[i].value;
    }

    return attributes;
    },
    getFirstElementByTagNS: function(node, ns, tag) {
    var el = node.getElementsByTagName(ns + ':' + tag)[0];

    if (!el)
    el = node.getElementsByTagNameNS(ns, tag)[0];

    if (!el)
    el = node.getElementsByTagName(tag)[0];

    return el;
    },
    getAttributeNS: function(node, ns, attribute) {
    var attr_value = node.getAttribute(ns + ':' + attribute);

    if (attr_value === undefined)
    attr_value = node.getAttributeNS(ns, attribute);

    if (attr_value === undefined)
    attr_value = node.getAttribute(attribute);

    return attr_value;
    },
    enforceType: function(type, value) {

    switch (type) {
    case 'boolean':
    value = (value === 'true');
    break;

    case 'integer':
    case 'long':
    case 'float':
    case 'double':
    value = +value;
    break;
    }

    return value;
    },
    getRGB: function(values) {
    return (values[3]) ?
    'rgba(' + values.join(',') + ')' :
    'rgb(' + values.slice(0, -1).join(',') + ')';
    }
    };


    /**
    * Parser Core Functions
    * ----------------------
    *
    * The XML parser's functions themselves.
    */

    /**
    * Node structure.
    * A function returning an object guarded with default value.
    *
    * @param {object} properties The node properties.
    * @return {object} The guarded node object.
    */
    function Node(properties) {

    // Possible Properties
    return {
    id: properties.id,
    label: properties.label,
    attributes: properties.attributes || {},
    viz: properties.viz || {}
    };
    }


    /**
    * Edge structure.
    * A function returning an object guarded with default value.
    *
    * @param {object} properties The edge properties.
    * @return {object} The guarded edge object.
    */
    function Edge(properties) {

    // Possible Properties
    return {
    id: properties.id,
    type: properties.type || 'undirected',
    label: properties.label || '',
    source: properties.source,
    target: properties.target,
    weight: +properties.weight || 1.0,
    viz: properties.viz || {}
    };
    }

    /**
    * Graph parser.
    * This structure parse a gexf string and return an object containing the
    * parsed graph.
    *
    * @param {string} xml The xml string of the gexf file to parse.
    * @return {object} The parsed graph.
    */
    function Graph(xml) {
    var _xml = {};

    // Basic Properties
    //------------------
    _xml.els = {
    root: xml.getElementsByTagName('gexf')[0],
    graph: xml.getElementsByTagName('graph')[0],
    meta: xml.getElementsByTagName('meta')[0],
    model: xml.getElementsByTagName('attribute'),
    nodes: xml.getElementsByTagName('node'),
    edges: xml.getElementsByTagName('edge')
    };

    _xml.hasViz = !!_helpers.getAttributeNS(_xml.els.root, 'xmlns', 'viz');
    _xml.version = _xml.els.root.getAttribute('version') || '1.0';
    _xml.mode = _xml.els.graph.getAttribute('mode') || 'static';

    var edgeType = _xml.els.graph.getAttribute('defaultedgetype');
    _xml.defaultEdgetype = edgeType || 'undirected';


    // Parser Functions
    //------------------

    // Meta Data
    function _metaData() {

    var metas = {};
    if (!_xml.els.meta)
    return metas;

    // Last modified date
    metas.lastmodifieddate = _xml.els.meta.getAttribute('lastmodifieddate');

    // Other information
    _helpers.nodeListEach(_xml.els.meta.childNodes, function(child) {
    metas[child.tagName.toLowerCase()] = child.textContent;
    });

    return metas;
    }

    // Model
    function _model() {
    var attributes = [];

    // Iterating through attributes
    _helpers.nodeListEach(_xml.els.model, function(attr) {

    // Properties
    var properties = {
    id: attr.getAttribute('id') || attr.getAttribute('for'),
    type: attr.getAttribute('type') || 'string',
    title: attr.getAttribute('title') || ''
    };

    // Defaults
    var default_el = _helpers.nodeListToArray(attr.childNodes);

    if (default_el.length > 0)
    properties.defaultValue = default_el[0].textContent;

    // Creating attribute
    attributes.push(properties);
    });

    return attributes;
    }

    // Nodes
    function _nodes(model) {
    var nodes = [];

    // Iteration through nodes
    _helpers.nodeListEach(_xml.els.nodes, function(n) {

    // Basic properties
    var properties = {
    id: n.getAttribute('id'),
    label: n.getAttribute('label') || ''
    };

    // Retrieving data from nodes if any
    if (model.length > 0)
    properties.attributes = _nodeData(model, n);

    // Retrieving viz information
    if (_xml.hasViz)
    properties.viz = _nodeViz(n);

    // Pushing node
    nodes.push(Node(properties));
    });

    return nodes;
    }

    // Data from nodes
    function _nodeData(model, node) {

    var data = {};
    var attvalues_els = node.getElementsByTagName('attvalue');

    // Getting Node Indicated Attributes
    var ah = _helpers.nodeListToHash(attvalues_els, function(el) {
    var attributes = _helpers.namedNodeMapToObject(el.attributes);
    var key = attributes.id || attributes['for'];

    // Returning object
    return {key: key, value: attributes.value};
    });


    // Iterating through model
    model.map(function(a) {

    // Default value?
    var att_title = a.title.toLowerCase();
    data[att_title] = !(a.id in ah) && 'defaultValue' in a ?
    _helpers.enforceType(a.type, a.defaultValue) :
    _helpers.enforceType(a.type, ah[a.id]);

    });

    return data;
    }

    // Viz information from nodes
    function _nodeViz(node) {
    var viz = {};

    // Color
    var color_el = _helpers.getFirstElementByTagNS(node, 'viz', 'color');

    if (color_el) {
    var color = ['r', 'g', 'b', 'a'].map(function(c) {
    return color_el.getAttribute(c);
    });

    viz.color = _helpers.getRGB(color);
    }

    // Position
    var pos_el = _helpers.getFirstElementByTagNS(node, 'viz', 'position');

    if (pos_el) {
    viz.position = {};

    ['x', 'y', 'z'].map(function(p) {
    viz.position[p] = +pos_el.getAttribute(p);
    });
    }

    // Size
    var size_el = _helpers.getFirstElementByTagNS(node, 'viz', 'size');
    if (size_el)
    viz.size = +size_el.getAttribute('value');

    // Shape
    var shape_el = _helpers.getFirstElementByTagNS(node, 'viz', 'shape');
    if (shape_el)
    viz.shape = shape_el.getAttribute('value');

    return viz;
    }

    // Edges
    function _edges(default_type) {
    var edges = [];

    // Iteration through edges
    _helpers.nodeListEach(_xml.els.edges, function(e) {

    // Creating the edge
    var properties = _helpers.namedNodeMapToObject(e.attributes);
    if (!('type' in properties)) {
    properties.type = default_type;
    }

    // Retrieving viz information
    if (_xml.hasViz)
    properties.viz = _edgeViz(e);

    edges.push(Edge(properties));
    });

    return edges;
    }

    // Viz information from edges
    function _edgeViz(edge) {
    var viz = {};

    // Color
    var color_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'color');

    if (color_el) {
    var color = ['r', 'g', 'b', 'a'].map(function(c) {
    return color_el.getAttribute(c);
    });

    viz.color = _helpers.getRGB(color);
    }

    // Shape
    var shape_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'shape');
    if (shape_el)
    viz.shape = shape_el.getAttribute('value');

    // Thickness
    var thick_el = _helpers.getFirstElementByTagNS(edge, 'viz', 'thickness');
    if (thick_el)
    viz.thickness = +thick_el.getAttribute('value');

    return viz;
    }


    // Returning the Graph
    //---------------------
    _xml.model = _model();

    return {
    version: _xml.version,
    mode: _xml.mode,
    defaultEdgeType: _xml.defaultEdgetype,
    meta: _metaData(),
    model: _xml.model,
    nodes: _nodes(_xml.model),
    edges: _edges(_xml.defaultEdgetype)
    };
    }


    /**
    * Public API
    * -----------
    *
    * User-accessible functions.
    */

    // Fetching GEXF with XHR
    function fetch(gexf_url, callback) {
    var xhr = (function() {
    if (window.XMLHttpRequest)
    return new XMLHttpRequest();

    var names,
    i;

    if (window.ActiveXObject) {
    names = [
    'Msxml2.XMLHTTP.6.0',
    'Msxml2.XMLHTTP.3.0',
    'Msxml2.XMLHTTP',
    'Microsoft.XMLHTTP'
    ];

    for (i in names)
    try {
    return new ActiveXObject(names[i]);
    } catch (e) {}
    }

    return null;
    })();

    if (!xhr)
    throw 'XMLHttpRequest not supported, cannot load the file.';

    // Async?
    var async = (typeof callback === 'function'),
    getResult;

    // If we can't override MIME type, we are on IE 9
    // We'll be parsing the response string then.
    if (xhr.overrideMimeType) {
    xhr.overrideMimeType('text/xml');
    getResult = function(r) {
    return r.responseXML;
    };
    }
    else {
    getResult = function(r) {
    var p = new DOMParser();
    return p.parseFromString(r.responseText, 'application/xml');
    };
    }

    xhr.open('GET', gexf_url, async);

    if (async)
    xhr.onreadystatechange = function() {
    if (xhr.readyState === 4)
    callback(getResult(xhr));
    };

    xhr.send();

    return (async) ? xhr : getResult(xhr);
    }

    // Parsing the GEXF File
    function parse(gexf) {
    return Graph(gexf);
    }

    // Fetch and parse the GEXF File
    function fetchAndParse(gexf_url, callback) {
    if (typeof callback === 'function') {
    return fetch(gexf_url, function(gexf) {
    callback(Graph(gexf));
    });
    } else
    return Graph(fetch(gexf_url));
    }


    /**
    * Exporting
    * ----------
    */
    this.GexfParser = {

    // Functions
    parse: parse,
    fetch: fetchAndParse,

    // Version
    version: '0.1'
    };

    }).call(this);