Coder Social home page Coder Social logo

d3-sankey-diagram's Introduction

d3-sankey-diagram

Build Status

Sankey diagrams show flows between processes or relationships between sets. This library, is a reusable d3 diagram featuring:

  • automatic layout
  • multiple types of flow
  • loops / reversed flows
  • flow routing across layers

See the demo for examples of these.

d3-sankey-diagram versions v0.6 and up are based on d3 v4.

Installation

Install using npm if you are using browserify or the like:

npm install d3-sankey-diagram

Or download the standalone bundle and include in your page as

<script src="d3-sankey-diagram.js" charset="utf-8"></script>

Usage

var layout = d3.sankey()
               .extent([[100, 10], [840, 580]]);

var diagram = d3.sankeyDiagram()
                .linkColor(function(d) { return d.color; });

d3.json('uk_energy.json', function(energy) {
  layout.ordering(energy.order);
  d3.select('#sankey')
      .datum(layout(energy))
      .call(diagram);
});

Try more live examples.

If you use the Jupyter notebook, try ipysankeywidget.

d3-sankey-diagram works both in node (using jsdom) and in the browser.

Contributing 🎁

Thanks for your interest in contributing! To get started see CONTRIBUTING.md and our code of conduct. We have a Roadmap showing what we are working on, and you can browse the list of good first issues for ideas.

Documentation

d3-sankey-diagram is a JavaScript library for creating Sankey diagrams using d3. See the live examples to get an idea of what it does.

The main high-level components:

Lower-level components:

Sankey layout

# sankey()

Creates a new Sankey layout component.

# layout(arguments...)

Apply the layout to the given arguments. The arguments are arbitrary; they are simply propagated to the accessor functions nodes and links.

var layout = d3.sankey();

var graph = layout({
  nodes: [
    {"id": "a", "title": "Source"},
    {"id": "b", "title": "Stage 1"}
  ],
  links: [
    {"source": "a", "target": "b", "type": "x", "value": 2}
  ]
});
// Resulting graph object can be used as datum for d3.sankeyDiagram() below

Data accessors

# layout.nodes([nodes])

If nodes is specified, sets the nodes accessor to the specified function and returns this layout. If nodes is not specified, return the current accessor, which defaults to:

function nodes(graph) {
  return graph.nodes;
}

# layout.links([links])

If links is specified, sets the links accessor to the specified function and returns this layout. If links is not specified, return the current accessor, which defaults to:

function links(graph) {
  return graph.links;
}

# layout.nodeId([nodeId])

If nodeId is specified, sets the node id accessor to the specified function and returns this layout. If nodeId is not specified, return the current accessor, which defaults to:

function nodeId(d) {
  return d.id;
}

# layout.sourceId([sourceId])
# layout.targetId([targetId])

If sourceId/targetId is specified, sets the link source/target id accessor to the specified function and returns this layout. If sourceId/targetId is not specified, return the current accessor, which defaults to:

function sourceId (d) {
  return {
    id: typeof d.source === 'object' ? d.source.id : d.source,
    port: typeof d.sourcePort === 'object' ? d.sourcePort.id : d.sourcePort
  }
}
// similarly for targetId

See below for more discussion of ports. If this accessor returns a string, it is interpreted as the node id and the port is set to undefined.

# layout.nodeBackwards([nodeBackwards])

If nodeBackwards is specified, sets the node direction accessor to the specified function and returns this layout. If nodeBackwards is not specified, return the current accessor, which defaults to:

function nodeBackwards(d) {
  return d.direction && d.direction.toLowerCase() === 'l';
}

# layout.linkValue([linkValue])

If linkValue is specified, sets the link value accessor to the specified function and returns this layout. If linkValue is not specified, return the current accessor, which defaults to:

function linkValue(d) {
  return d.value;
}

# layout.linkType([linkType])

If linkType is specified, sets the link type accessor to the specified function and returns this layout. If linkType is not specified, return the current accessor, which defaults to:

function linkType(d) {
  return d.type;
}

Adjusting layout

# layout.ordering([ordering])

If ordering is specified, sets the node ordering to the specified value and returns this layout. If ordering is not specified, return the current value, which defaults to null.

When ordering is null, the node ordering will be calculated automatically.

When ordering is specified, it is used directly and no rank assignment or ordering algorithm takes place. The ordering structure has three nested lists: ordering is a list of layers, each of which is a list of bands, each of which is a list of node ids. For example,

[
  [ ["layer 1 band 1"], ["layer 1 band 2"] ],
  [ ["layer 2 band 1"], ["layer 2 band 2"] ],
  ...
]

# layout.rankSets([rankSets])

If rankSets is specified, sets the rank sets to the specified value and returns this layout. If rankSets is not specified, return the current value, which defaults to [].

Rank sets are optional constraints to keep nodes in the same layer. Each entry has the form

{
    type: 'same|min',   // optional, default 'min'
    nodes: [node ids],  // required
}

# layout.sortPorts([sortPorts])

If sortPorts is specified, sets the port sorting function to the specified function and returns this layout. If sortPorts is not specified, return the current value, which defaults to:

function sortPorts(a, b) {
  return a.id.localeCompare(b.id)
}

Note: in a future version this may be changed to sort ports to avoid crossings by default.

Dimensions

# layout.extent([extent])

If extent is specified, sets this layout’s extent to the specified array of points [[x0, y0], [x1, y1]], where [x0, y0] is the top-left corner and [x1, y1] is the bottom-right corner, and returns this tile layout. If extent is not specified, returns the current layout extent.

# layout.size([size])

If size is specified, sets this layout’s size to the specified two-element array of numbers [width, height] and returns this layout. If size is not specified, returns the current layout size. This is a convenience method equivalent to setting the extent to [[0, 0], [width, height]].

# diagram.scale([scale])

If scale is specified as a number, sets the layout's scale (from data units to pixels). If scale is null, the scale will be reset and automatically calculated the next time the diagram is called, to achieve the desired whitespace fraction (below). If scale is not specified, return the current scale.

# diagram.whitespace([whitespace])

If whitespace is specified as a number, sets the layout's whitespace fraction, used when automatically calculating a scale (above). If whitespace is not specified, return the current whitespace.

Manual positioning

# diagram.nodePosition([nodePosition])

If nodePosition is specified, use the specified function to directly set node positions, bypassing the layout algorithm (link positions and shapes are still calculated). If nodePosition is not specified, return the current function, which defaults to null.

SVG Sankey diagram component

# sankeyDiagram()

Creates a new Sankey diagram component.

# diagram(selection)

Apply the diagram to a selection, which should be an svg element.

var diagram = d3.sankeyDiagram();
d3.select('#sankey')
    .datum(sankey)
    .call(diagram);

The Sankey data is taken from the selection's bound data, which should be a graph object, as generated by d3.sankey.

Dimensions

# diagram.margin({ [top], [right], [bottom], [left] })

If called with an argument, set the margins of the diagram, otherwise return the current value.

Titles

# diagram.nodeTitle([nodeTitle])

If called with an argument, set the node title to the specified function, otherwise return the current function, which defaults to:

function nodeTitle(d) {
  return d.title !== undefined ? d.title : d.id;
}

# diagram.nodeValue([nodeValue])

If called with an argument, set the node value getter to the specified function, otherwise return the current function, which defaults to:

function nodeValue(d) {
  return null;
}

The node value is shown with an SVG text element within the node body, only when the nodeWidth is greater than zero.

# diagram.linkTitle([linkTitle])

If called with an argument, set the link title to the specified function, otherwise return the current function, which defaults to:

const fmt = d3.format('.3s')
function linkTitle(d) {
  const parts = []
  const sourceTitle = nodeTitle(d.source)
  const targetTitle = nodeTitle(d.target)
  const matTitle = d.type

  parts.push(`${sourceTitle}${targetTitle}`)
  if (matTitle) parts.push(matTitle)
  parts.push(fmt(d.value))
  return parts.join('\n')
}

The link title is displayed in an SVG title element, visible on hover.

To make it easier to customise this function to your data, you can use d3.sankeyLinkTitle to generate new functions:

# sankeyLinkTitle(nodeTitle, typeTitle, fmt)

Generates a function similar to the one above, with custom accessors for the node title nodeTitle, link-type title typeTitle and number format fmt.

# diagram.linkLabel([linkLabel])

If called with an argument, set the link label to the specified function, otherwise return the current function, which defaults to:

function linkLabel(d) {
  return null
}

The link label is displayed in an SVG text element, so unlike the /title/, it is visible all the time.

Link appearance

# diagram.linkColor([linkColor])

If linkColor is specified, sets the link color accessor to the specified function, otherwise return the current accessor, which defaults to:

function linkColor(d) {
  return null;
}

# diagram.linkMinWidth([linkMinWidth])

If linkMinWidth is specified, sets the minimum link width accessor to the specified function, otherwise return the current accessor, which by default returns 1.

Node groups

# diagram.groups([groups])

If groups is specified, sets the list of node groups to the specified value. If groups is not specified, return the current list. Node groups display a box around the specified nodes, and should be given in the following format:

[
    { title: "Group title to be displayed above nodes",
      nodes: ["nodeid1", "nodeid", ...]
    }
]

Events

# diagram.on(type[, listener])

Adds or removes an event listener for the specified type. The type string is one of selectNode, selectLink or selectGroup. The listener is invoked with the context as the element and one argument, the corresponding data.

If listener is not specified, returns the currently-assigned listener for the specified type, if any.

Tests

Run the tests:

npm test

Licence

MIT licence.

Contributors

  • Rick Lupton
  • @harisbal
  • @svwielga4

Changelog

Unreleased

v0.8.0

  • Modified code to assign type and value attributes to link objects (thanks @harisbal)
  • EXPERIMENTAL: allow short "stub" flows in or out of nodes using fromElsewhere and toElsewhere attributes. These can be useful for simplifying diagrams by avoiding uninteresting flows while still showing how nodes balance.

v0.7.3

  • Fix packaging to avoid overwriting the d3 global object in UMD module!
  • Add a basic link label feature to show the values of links with SVG text elements. See diagram.linkLabel. (#2)

v0.7.2

  • Update packaging to produce different module builds:
    • d3-sankey-diagram.esm.js: An ES module
    • d3-sankey-diagram.cjs.js: A CommonJS module for use with NodeJS
    • d3-sankey-diagram.umd.js: A UMD module for use in <script> tags
    • d3-sankey-diagram.min.js: A minified version of the UMD module

v0.7.1

  • Unused code tidied up (thanks svwielga4)
  • Improved node value labels: with wide nodes, the labels are shown within the nodes. Otherwise they are now shown after the node titles in parentheses. See example.
  • By default, node values are not shown by default (the default in v0.7.0 was to show them). Use diagram.nodeValue to set the format to show them.

v0.7.0

d3-sankey-diagram's People

Contributors

harisbal avatar ricklupton avatar sliceking avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

d3-sankey-diagram's Issues

[info] Compatibility with version 7 of d3js

Hello,
I managed to update the bundle d3-sankey-diagram.js to make it compatible with the version 7 of d3.js.

Main changes is that d3-collection no longer exists. See migration guide for version 6, here, still working for version 7 https://observablehq.com/@d3/d3v6-migration-guide#collection

Modified file:
d3-sankey-diagram.js.txt

Changes:

  • d3Collection.map() >>> new Map() [7 occurences]
  • d3Collection.set() >>> new Set() [3 occurences]
  • line 19250 node.ports = ports.values() >>> node.ports = ports
  • line 19251 node.ports.sort(sortPorts) >>> line removed - didn't find better fix

With these changes, that example https://bl.ocks.org/ricklupton/6344d23aa6418702f120b34343dce7cb works.

Basic example on wiki does work - crashes with TypeError: Cannot read properties of undefined (reading 'length')

Using the basic example on https://ricklupton.github.io/d3-sankey-diagram/, I get the following:

-sankey-diagram.umd.js:20584 Uncaught TypeError: Cannot read properties of undefined (reading 'length')
at titlePosition (d3-sankey-diagram.umd.js:20584:1)
at HTMLUnknownElement.eval (d3-sankey-diagram.umd.js:20467:1)
at Selection.eval [as each] (each.js:5:1)
at sankeyNode (d3-sankey-diagram.umd.js:20458:1)
at Selection.eval [as call] (call.js:4:1)
at updateNodes (d3-sankey-diagram.umd.js:20744:1)
at HTMLDivElement.eval (d3-sankey-diagram.umd.js:20711:1)
at Wn.each (d3.min.js:2:25626)
at exports (d3-sankey-diagram.umd.js:20674:1)
at Wn.call (d3.min.js:2:25196)
t

0.8.0 d3-sankey-diagram
7.8.4 d3

extent difficult to compute

the extent() property nicely bounds the verticals for the chart, but it doesn't account for labels and paths which happen to appear outside of the area bounded by those verticals. as a result i need to manually set an extent after i've seen a rending to know how much padding i'm going to need to properly render the chart without obscuring the labels and paths which appear outside the extent. it would nice to have a way to bound the chart which included these outlying labels and paths so i can ensure my entire chart is plotted inside my target rectangle.

Support for links with gradient?

With d3-sankey I have to manually draw the svg for the links, but this does give me more control, for example to add gradients. What would be the best way to tackle this in d3-sankey-diagram?

I've used Vue to handle the actual drawing of the svg, here's an example of te links drawing part, where I include a linearGradient per link:

<g id="links" fill="none" stroke-opacity="0.5">
    <g
    v-for="link in links"
    :key="`${link.source.name}>${link.target.name}`"
    style="mix-blend-mode: multiply"
    >
    <linearGradient
        :id="link.uid"
        gradientUnits="userSpaceOnUse"
        :x1="link.source.x1"
        :x2="link.target.x0"
    >
        <stop offset="0%" :stop-color="color(link.source.name)"></stop>
        <stop offset="100%" :stop-color="color(link.target.name)"></stop>
    </linearGradient>
    <path
        :d="pathData(link)"
        :stroke="`url(#${link.uid})`"
        :stroke-width="Math.max(minStrokeWidth, link.width)"
    ></path>
    <title>{{ linkTitleText(link) }}</title>
    </g>
</g>

(This approach also works when loading the svg into applications like Inkscape and Illustrator)

d3-sankey migration guide?

I might be missing a piece of the puzzle but I'm looking for the source of a complete example using d3-sankey-diagram.
I'm mostly missing the svg creation part.

This is the source of the github page, but it's transpiled and therefore hard to read:
https://github.com/ricklupton/d3-sankey-diagram/blob/gh-pages/d3-sankey-diagram.js

I'm currently coming from d3-sankey where d3.sankeyLinkHorizontal is used to extract the svg pathdata for the links. This function doesn't seem to exist when using d3-sankey-diagram.

Duplicate links are not processed

If links includes duplicates as far as (source, target, type) is concerned, the later links aren't processed -- and you then get errors if you try to render them

Strange link layout gliches

Take this data:

{
  "nodes": [ 
    { "id": "a" }, { "id": "b" }, { "id": "c" }, {"id": "d" }, {"id": "e" }, {"id": "f" }
  ],
  "links": [
    { "source": "a", "target": "b", "value": 1 },
    { "source": "a", "target": "c", "value": 1000 }, 
    { "source": "b", "target": "d", "value": 1 },
    { "source": "c", "target": "d", "value": 1000 },
    { "source": "d", "target": "e", "value": 1 },
    { "source": "e", "target": "f", "value": 1 }
  ]
}

put into https://ricklupton.github.io/d3-sankey-diagram/

Observe strange layout if links from "a" to "c" (largest one)

Upgrade to d3 v4

d3 version 4 is much more modular and changes some APIs.

While upgrading, I'm planning to revise the diagram API and bring the sankey-layout package into this package, with a clean separation between graph layout (ranking and ordering node), Sankey node/link layout (curve radii etc) and SVG components.

Ability to pass custom labels for linkTitle

Love the package so far, but would be great to be able to pass custom strings to append to the label (i.e. d.custlabel) when passing the links. Use case for me would be for including in addition to the total count the percent of records from a particular node moving down a given forward.

Current implementation

const fmt = d3.format('.3s')
function linkTitle(d) {
  const parts = []
  const sourceTitle = nodeTitle(d.source)
  const targetTitle = nodeTitle(d.target)
  const matTitle = d.type

  parts.push(`${sourceTitle} → ${targetTitle}`)
  if (matTitle) parts.push(matTitle)
  parts.push(fmt(d.value))
  return parts.join('\n')
}

Proposed

const fmt = d3.format('.3s')
function linkTitle(d) {
  const parts = []
  const sourceTitle = nodeTitle(d.source)
  const targetTitle = nodeTitle(d.target)
  const matTitle = d.type
  const custTitle = d.custlabel

  parts.push(`${sourceTitle} → ${targetTitle}`)
  if (matTitle) parts.push(matTitle)
  parts.push(fmt(d.value))
  if (custTitle) parts.push(custTitle)
  return parts.join('\n')
}

Deal better with incremental layout & rendering

Currently, it works fine if we do all the layout on a new graph, OR the graph is supplied with nodePositions set. But it's difficult to do little bits.

For example,

  • when dragging a node to tweak the layout of a diagram, I want to recalculate the link ordering and radii for just that node (or maybe the neighbours too)
  • when reloading a tweaked layout, I want to use predefined node positions and link point coordinates, but still setup the 'linked' structure of the graph object (link source and target as node objects, node incoming and outgoing as lists)

fsevents dependency prohibits installation on linux

$ npm install d3-sankey-diagram

npm ERR! code EBADPLATFORM
npm ERR! notsup Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm ERR! notsup Valid OS:    darwin
npm ERR! notsup Valid Arch:  any
npm ERR! notsup Actual OS:   linux
npm ERR! notsup Actual Arch: x64

The use of fsevents is unclear and since it was added in conjunction with the addition of prettier, it is probably not needed as a dependency.

https://github.com/ricklupton/d3-sankey-diagram/blame/d8a126e9fc2bf4c4b98cc444544068bbd6c4d283/package.json#L37

Calling `update()` breaks circular link paths

First of all, thank you for this very nice library @ricklupton

I have seen a strange behavior when using update: when calling sankey.update(graph) with an unmodified graph object, circular links (usually made out of 4 segments) are now made out of 2 segments.

The layout goes from:
screenshot_2019-02-04_14-07-10
to the following wrong result upon calling update:
screenshot_2019-02-04_14-07-22

Programmatically, this can be seen by running the following:

            var graph = sankey(data);
            var before = graph.links.map(function(link) {
                return link.points.map(function(pts) {
                    return pts.d;
                });
            });
            console.log(before);
            // [['r', 'r'], ['r', 'r', 'r', 'r'], ['r', 'r'], ['r', 'r'], ['r', 'l', 'l', 'r'], ['r', 'r'], ['r', 'l', 'l', 'r']]

            graph = sankey.update(graph);
            var after = graph.links.map(function(link) {
              return link.points.map(function(pts) {
                  return pts.d;
              });
            });
            console.log(after);
            // [['r', 'r'], ['r', 'r'], ['r', 'r'], ['r', 'r'], ['r', 'r'], ['r', 'r'], ['r', 'r']]

Angular 5 integration

Hi @ricklupton,

Thanks for your great library. It's really awesome.
I would like to integrate it with Angular 5.
Have you tested if this works with Angular 5?

Thanks

Direction not consistent when grouping nodes

Hi @ricklupton ,

Really great visualization!

When grouping nodes the direction of the links seem to do whatever direction suits best. I would like them to always go right side out left side in. In many cases this is going OK but in this example the diagram doing it (for me) the wrong way.

image

Is there any way in correcting this?

I tried direction: "r" on the nodes with no success.

This is part(!) of my JSON:"

{
    "nodes": [{
            "id": "01 - Homepage",
            "direction": "r"
        },
        {
            "id": "02 - Park - Homepages",
            "direction": "r"
        },
        {
            "id": "03 - Park - Content pages",
            "direction": "r"
        },
        {
            "id": "04 - Park - Acco(detail)",
            "direction": "r"
        }
    ],
    "links": [{
            "source": "Enter",
            "target": "02 - Park - Homepages",
            "value": 64223,
            "type": "enter"
        },
        {
            "source": "02 - Park - Homepages",
            "target": "04 - Park - Acco(detail)",
            "value": 55397,
            "type": "homepage"
        },
        {
            "source": "04 - Park - Acco(detail)",
            "target": "Exit",
            "value": 45991,
            "type": "exit"
        },
        {
            "source": "02 - Park - Homepages",
            "target": "Exit",
            "value": 38980,
            "type": "exit"
        },
        {
            "source": "Enter",
            "target": "01 - Homepage",
            "value": 34687,
            "type": "enter"
        },
        {
            "source": "02 - Park - Homepages",
            "target": "03 - Park - Content pages",
            "value": 30135,
            "type": "homepage"
        }
    ],
    "groups": [{
            "type": "Group",
            "title": "Enter",
            "bundle": null,
            "id": "Enter",
            "nodes": ["Enter"],
            "def_pos": null
        },
        {
            "type": "Group",
            "title": "Broad - Shopping",
            "bundle": null,
            "id": "Broad",
            "nodes": ["01 - Homepage", "05 - Theme - Home", "06 - Theme - Content", "07 - Offer - Overview", "08 - Offer - Detail", "09 - Search - Lists results", "11 - Maps", "14 - Site-Content"],
            "def_pos": null
        },
        {
            "type": "Group",
            "title": "Narrow - Shopping",
            "bundle": null,
            "id": "Narrow",
            "nodes": ["02 - Park - Homepages", "03 - Park - Content pages", "04 - Park - Acco(detail)", "15 - Compare"],
            "def_pos": null
        },
        {
            "type": "Group",
            "title": "Booking",
            "bundle": null,
            "id": "Booking",
            "nodes": ["12 - IBE", "17 - My Vacation", "NoMatch"],
            "def_pos": null
        },
        {
            "type": "Group",
            "title": "Pre Holiday",
            "bundle": null,
            "id": "Pre Holiday",
            "nodes": ["13 - My Account"],
            "def_pos": null
        },
        {
            "type": "Group",
            "title": "Exit",
            "bundle": null,
            "id": "Exit",
            "nodes": ["Exit"],
            "def_pos": null
        }
    ],
    "order": [
        [
            ["Enter"],
            [],
            []
        ],
        [
            ["01 - Homepage", "05 - Theme - Home", "06 - Theme - Content", "07 - Offer - Overview", "08 - Offer - Detail", "09 - Search - Lists results", "11 - Maps", "14 - Site-Content"],
            [],
            []
        ],
        [
            ["02 - Park - Homepages", "03 - Park - Content pages", "04 - Park - Acco(detail)", "15 - Compare"],
            [],
            []
        ],
        [
            ["12 - IBE", "17 - My Vacation", "NoMatch"],
            [],
            []
        ],
        [
            ["13 - My Account"],
            [],
            []
        ],
        [
            ["Exit"],
            [],
            []
        ]
    ],
    "rankSets": [{
            "type": "min",
            "nodes": ["Enter"]
        },
        {
            "type": "min",
            "nodes": ["01 - Homepage", "05 - Theme - Home", "06 - Theme - Content", "07 - Offer - Overview", "08 - Offer - Detail", "09 - Search - Lists results", "11 - Maps", "14 - Site-Content"]
        },
        {
            "type": "min",
            "nodes": ["02 - Park - Homepages", "03 - Park - Content pages", "04 - Park - Acco(detail)", "15 - Compare"]
        },
        {
            "type": "min",
            "nodes": ["12 - IBE", "17 - My Vacation", "NoMatch"]
        },
        {
            "type": "min",
            "nodes": ["13 - My Account"]
        }, {
            "type": "min",
            "nodes": ["Exit"]
        }
    ]
}

More clearly defined expected nodes and links data

This one kind of builds on #22. Node groups clearly states "should be given in the following format" which then gives a clear example.

I noticed that the expected format for links, nodes data on the other hand is kind of undocumented. Especially since there are properties like the following which are otherwise only documented elsewhere.

  • linkstype Used in default linkTitle
  • nodesdirection Used in default nodeBackwards

Undocumented and unused fields in example json

The following json file is used as example:
https://ricklupton.github.io/d3-sankey-diagram/uk_energy.json

I'm seeing a lot of unused and undocumented fields:

  • links:
    • time (seems unused and undocumented, always set to "*")
    • type (used in default linkTitle but otherwise not documented)
    • title (seems unused and undocumented)
  • groups:
    • bundle (seems unused and undocumented, always set to null)
    • def_pos (seems unused and undocumented, always set to null)
    • id (seems unused and undocumented)
    • type (seems unused and undocumented)
  • nodes:
    • bundle (seems unused and undocumented, always set to null)
    • def_pos (seems unused and undocumented, always set to null)
    • visibility (seems unused and undocumented, always set to visible)
    • style (seems unused and undocumented, always set to visible)
    • direction (used by nodeBackwards but otherwise not documented)

Export

Is it possible to export the generated diagram as an image like a SVG or similar?

Allow custom classes to be added to nodes, links

What do you think about there being a customisation method to support adding CSS classes to nodes and links? This should (I think) then allow the image styling to be performed in CSS rather than via color settings in the data config.

Great work on extending the original D3 sankey lib :)

> 1 nodeWidth creates angled circular link paths

When a nodeWidth of more than 1 is used some circular link paths are drawn angled / rotated.

nodeWidth of 1

Screenshot from 2019-09-05 13-29-28@2x

nodeWidth of 30

Screenshot from 2019-09-05 13-29-01@2x

Example code:
https://codesandbox.io/s/nodewidth-d3-sankey-diagram-phmpr

Copy of example code

import * as d3Base from "d3";
import * as sankey from "d3-sankey-diagram";

const d3 = {
  ...d3Base,
  ...sankey
};

var data = {
  nodes: [
    {
      id: "a",
      title: "a",
      direction: "r"
    },
    {
      id: "b",
      title: "b",
      direction: "r"
    },
    {
      id: "c",
      title: "c",
      direction: "r"
    }
  ],
  links: [
    {
      source: "a",
      target: "b",
      value: 1
    },
    {
      source: "b",
      target: "c",
      value: 1.2
    },
    {
      source: "c",
      target: "a",
      value: 0.2
    }
  ]
};

var svg = d3.select("svg");
var width = +svg.attr("width");
var height = +svg.attr("height");
var margin = { top: 10, left: 50, bottom: 10, right: 50 };

var layout = d3
  .sankey()
  .nodeWidth(30)
  .extent([
    [margin.left, margin.top],
    [width - margin.left - margin.right, height - margin.top - margin.bottom]
  ]);

// Render
var color = d3.scaleOrdinal(d3.schemeCategory10);
var diagram = d3
  .sankeyDiagram()
  .linkMinWidth(() => 0.1)
  .linkColor(d => color(d.type));

svg.datum(layout(data)).call(diagram);

Allow customizing the link title

I'm aware you can customize part of the link title using linkTypeTitle but I couldn't find a way of completely configuring the link title.

Is it possible to use the params data to move nodes

Hi,

In the original d3-sankey it is possible to store specific position of each nodes, as you can see in https://sankey.csaladen.es/.

The data structure uses the following format:

params":[0.5,0.25,0,0,0],"fixedlayout":[[400.5,218.78016596921236],[0,497.0588235294118],[0,61.70588235294119],[587.25,0],[195.75,274.87527492724956],[790,0],[581.75,0]]}

Can this be used in the d3-sankey-diagram?

Remove unused code from linkPath.js

excuse me, when i read the source code.
i wonder why if (true) {...} else {...}?
test code?

if (true) {
path = ("M" + [x0, y0-h ] + " " +
arc(+1, r0) + [x2+hs, y2-hc] + " " +
"L" + [x3+hs, y3-hc] + " " +
arc(-1, r1) + [x1, y1-h ] + " " +
"L" + [x1, y1+h ] + " " +
arc(+1, r1) + [x3-hs, y3+hc] + " " +
"L" + [x2-hs, y2+hc] + " " +
arc(-1, r0) + [x0, y0+h ] + " " +
"Z");
} else {
// keep same number of points
theta = Math.abs(theta);
path = ("M" + [x0, y0-h ] + " " +
arc(+1, r0) + [x1, y1-h ] + " " +
"L" + [x1, y1-h ] + " " +
arc(-1, r1) + [x1, y1-h ] + " " +
"L" + [x1, y1+h ] + " " +
arc(+1, r1) + [x0, y0+h ] + " " +
"L" + [x0, y0+h ] + " " +
arc(-1, r0) + [x0, y0+h ] + " " +
"Z");
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.