Search code examples
javascriptsvgd3.jsforce-layoutbundle-layout

Hierarchical Edge Bundling from Force Layout in D3


I currently have a Force Layout:

http://bl.ocks.org/mbostock/4062045

So my data is simply nodes and links, but I'd like to create this Hierarchical Edge Bundling with the same data:

http://bl.ocks.org/mbostock/7607999

enter image description here

I'm having issues because my data isn't hierarchical. Its just nodes and links. I'm not sure how to make this work, but it must be possible.


Solution

  • I believe the best approach would be to modify the data you have before applying bundle layout.

    Specifically, you should be able to introduce a fictional root to make your data hierarchical. The root node could be:

    a) parent for all nodes in your current data that have at least one child

    or

    b) parent for all nodes in your current data that do not have a parent.

    What is better depends on your case (data/application).

    While converting the data, you need to take care of two things on top of that: make sure your original data does not have cycles (that would prevent converting it to hierarchy), and you need to follow data format for bundle layout (for example, see json file http://bl.ocks.org/mbostock/raw/1044242/readme-flare-imports.json).

        [
          {
            "name":"flare.analytics.cluster.AgglomerativeCluster",
            "size":3938,
            "imports":[
              "flare.animate.Transitioner",
              "flare.vis.data.DataList",
              "flare.util.math.IMatrix",
              "flare.analytics.cluster.MergeEdge",
              "flare.analytics.cluster.HierarchicalCluster",
              "flare.vis.data.Data"
            ]
          },
          {
            "name":"flare.analytics.cluster.CommunityStructure",
            "size":3812,
            "imports":[
              "flare.analytics.cluster.HierarchicalCluster",
              "flare.animate.Transitioner",
              "flare.vis.data.DataList",
              "flare.analytics.cluster.MergeEdge",
              "flare.util.math.IMatrix"
            ]
          },
          {
            "name":"flare.analytics.cluster.HierarchicalCluster",
            "size":6714,
            "imports":[
              "flare.vis.data.EdgeSprite",
              "flare.vis.data.NodeSprite",
              "flare.vis.data.DataList",
              "flare.vis.data.Tree",
              "flare.util.Arrays",
              "flare.analytics.cluster.MergeEdge",
              "flare.util.Sort",
              "flare.vis.operator.Operator",
              "flare.util.Property",
              "flare.vis.data.Data"
            ]
          },
          {
            "name":"flare.analytics.cluster.MergeEdge",
            "size":743,
            "imports":[
    
            ]
          },
          {
            "name":"flare.analytics.graph.BetweennessCentrality",
            "size":3534,
            "imports":[
              "flare.animate.Transitioner",
              "flare.vis.data.NodeSprite",
              "flare.vis.data.DataList",
              "flare.util.Arrays",
              "flare.vis.data.Data",
              "flare.util.Property",
              "flare.vis.operator.Operator"
            ]
          },
          {
            "name":"flare.analytics.graph.LinkDistance",
            "size":5731,
            "imports":[
              "flare.animate.Transitioner",
              "flare.vis.data.NodeSprite",
              "flare.vis.data.EdgeSprite",
              "flare.analytics.graph.ShortestPaths",
              "flare.vis.data.Data",
              "flare.util.Property",
              "flare.vis.operator.Operator"
            ]
          },
          {
            "name":"flare.analytics.graph.MaxFlowMinCut",
            "size":7840,
            "imports":[
              "flare.animate.Transitioner",
              "flare.vis.data.NodeSprite",
              "flare.vis.data.EdgeSprite",
              "flare.vis.data.Data",
              "flare.util.Property",
              "flare.vis.operator.Operator"
            ]
          },
          {
            "name":"flare.analytics.graph.ShortestPaths",
            "size":5914,
            "imports":[
              "flare.vis.data.EdgeSprite",
              "flare.vis.data.NodeSprite",
              "flare.animate.Transitioner",
              "flare.vis.operator.Operator",
              "flare.util.heap.HeapNode",
              "flare.util.heap.FibonacciHeap",
              "flare.util.Property",
              "flare.vis.data.Data"
            ]
          },
          {
            "name":"flare.analytics.graph.SpanningTree",
            "size":3416,
            "imports":[
              "flare.animate.Transitioner",
              "flare.vis.data.NodeSprite",
              "flare.vis.operator.IOperator",
              "flare.vis.Visualization",
              "flare.vis.data.TreeBuilder",
              "flare.vis.operator.Operator"
            ]
          },
          {
            "name":"flare.analytics.optimization.AspectRatioBanker",
            "size":7074,
            "imports":[
              "flare.animate.Transitioner",
              "flare.util.Arrays",
              "flare.vis.data.DataSprite",
              "flare.scale.Scale",
              "flare.vis.axis.CartesianAxes",
              "flare.vis.Visualization",
              "flare.util.Property",
              "flare.vis.operator.Operator"
            ]
          },
          {
            "name":"flare.animate.Easing",
            "size":17010,
            "imports":[
              "flare.animate.Transition"
            ]
          },
          {
            "name":"flare.animate.FunctionSequence",
            "size":5842,
            "imports":[
              "flare.util.Maths",
              "flare.animate.Transition",
              "flare.util.Arrays",
              "flare.animate.Sequence",
              "flare.animate.Transitioner"
            ]
          },
          {
            "name":"flare.animate.interpolate.ArrayInterpolator",
            "size":1983,
            "imports":[
              "flare.util.Arrays",
              "flare.animate.interpolate.Interpolator"
            ]
          },
          {
            "name":"flare.animate.interpolate.ColorInterpolator",
            "size":2047,
            "imports":[
              "flare.animate.interpolate.Interpolator"
            ]
          },
          {
            "name":"flare.animate.interpolate.DateInterpolator",
            "size":1375,
            "imports":[
              "flare.animate.interpolate.Interpolator"
            ]
          },
          {
            "name":"flare.animate.interpolate.Interpolator",
            "size":8746,
            "imports":[
              "flare.animate.interpolate.NumberInterpolator",
              "flare.animate.interpolate.ColorInterpolator",
              "flare.animate.interpolate.PointInterpolator",
              "flare.animate.interpolate.ObjectInterpolator",
              "flare.animate.interpolate.MatrixInterpolator",
              "flare.animate.interpolate.RectangleInterpolator",
              "flare.animate.interpolate.DateInterpolator",
              "flare.util.Property",
              "flare.animate.interpolate.ArrayInterpolator"
            ]
          },
          {
            "name":"flare.animate.interpolate.MatrixInterpolator",
            "size":2202,
            "imports":[
              "flare.animate.interpolate.Interpolator"
            ]
          },
          {
            "name":"flare.animate.interpolate.NumberInterpolator",
            "size":1382,
            "imports":[
              "flare.animate.interpolate.Interpolator"
            ]
          },
          {
            "name":"flare.animate.interpolate.ObjectInterpolator",
            "size":1629,
            "imports":[
              "flare.animate.interpolate.Interpolator"
            ]
          },
          {
            "name":"flare.animate.interpolate.PointInterpolator",
            "size":1675,
            "imports":[
              "flare.animate.interpolate.Interpolator"
            ]
          },
          {
            "name":"flare.animate.interpolate.RectangleInterpolator",
            "size":2042,
            "imports":[
              "flare.animate.interpolate.Interpolator"
            ]
          },
          {
            "name":"flare.animate.ISchedulable",
            "size":1041,
            "imports":[
              "flare.animate.Scheduler"
            ]
          },
          {
            "name":"flare.animate.Parallel",
            "size":5176,
            "imports":[
              "flare.animate.Easing",
              "flare.animate.Transition",
              "flare.util.Arrays"
            ]
          },
          {
            "name":"flare.animate.Pause",
            "size":449,
            "imports":[
              "flare.animate.Transition"
            ]
          },
          {
            "name":"flare.animate.Scheduler",
            "size":5593,
            "imports":[
              "flare.animate.ISchedulable",
              "flare.animate.Pause",
              "flare.animate.Transition"
            ]
          },
          {
            "name":"flare.animate.Sequence",
            "size":5534,
            "imports":[
              "flare.animate.Easing",
              "flare.util.Maths",
              "flare.animate.Transition",
              "flare.util.Arrays"
            ]
          },
          {
            "name":"flare.animate.Transition",
            "size":9201,
            "imports":[
              "flare.animate.Transitioner",
              "flare.animate.TransitionEvent",
              "flare.animate.Scheduler",
              "flare.animate.Pause",
              "flare.animate.Parallel",
              "flare.animate.Easing",
              "flare.animate.Sequence",
              "flare.animate.ISchedulable",
              "flare.util.Maths",
              "flare.animate.Tween"
            ]
          },
          {
            "name":"flare.animate.Transitioner",
            "size":19975,
            "imports":[
              "flare.util.IValueProxy",
              "flare.animate.Parallel",
              "flare.animate.Easing",
              "flare.animate.Sequence",
              "flare.animate.Transition",
              "flare.animate.Tween",
              "flare.util.Property"
            ]
          },
          {
            "name":"flare.animate.TransitionEvent",
            "size":1116,
            "imports":[
              "flare.animate.Transition"
            ]
          },
          {
            "name":"flare.animate.Tween",
            "size":6006,
            "imports":[
              "flare.animate.Transitioner",
              "flare.animate.Transition",
              "flare.animate.interpolate.Interpolator",
              "flare.util.Property"
            ]
          },
          {
            "name":"flare.data.converters.Converters",
            "size":721,
            "imports":[
              "flare.data.converters.IDataConverter",
              "flare.data.converters.GraphMLConverter",
              "flare.data.converters.JSONConverter",
              "flare.data.converters.DelimitedTextConverter"
            ]
          },
          {
            "name":"flare.data.converters.DelimitedTextConverter",
            "size":4294,
            "imports":[
              "flare.data.DataSet",
              "flare.data.DataUtil",
              "flare.data.DataTable",
              "flare.data.converters.IDataConverter",
              "flare.data.DataSchema",
              "flare.data.DataField"
            ]
          },
          {
            "name":"flare.data.converters.GraphMLConverter",
            "size":9800,
            "imports":[
              "flare.data.DataSet",
              "flare.data.DataUtil",
              "flare.data.DataTable",
              "flare.data.converters.IDataConverter",
              "flare.data.DataSchema",
              "flare.data.DataField"
            ]
          },
          {
            "name":"flare.data.converters.IDataConverter",
            "size":1314,
            "imports":[
              "flare.data.DataSet",
              "flare.data.DataSchema"
            ]
          },
          {
            "name":"flare.data.converters.JSONConverter",
            "size":2220,
            "imports":[
              "flare.data.DataSet",
              "flare.data.DataUtil",
              "flare.data.DataTable",
              "flare.data.converters.IDataConverter",
              "flare.data.DataSchema",
              "flare.data.DataField",
              "flare.util.Property"
            ]
          },
          {
            "name":"flare.data.DataField",
            "size":1759,
            "imports":[
              "flare.data.DataUtil"
            ]
          },
          {
            "name":"flare.data.DataSchema",
            "size":2165,
            "imports":[
              "flare.data.DataField",
              "flare.util.Arrays"
            ]
          },
    .
    .
    .
          {
            "name":"flare.vis.Visualization",
            "size":16540,
            "imports":[
              "flare.animate.Transitioner",
              "flare.vis.operator.IOperator",
              "flare.animate.Scheduler",
              "flare.vis.events.VisualizationEvent",
              "flare.vis.data.Tree",
              "flare.vis.events.DataEvent",
              "flare.vis.axis.Axes",
              "flare.vis.axis.CartesianAxes",
              "flare.util.Displays",
              "flare.vis.operator.OperatorList",
              "flare.vis.controls.ControlList",
              "flare.animate.ISchedulable",
              "flare.vis.data.Data"
            ]
          }
        ]