Arquivo da categoria: Java Script

D3js v4 + Vuejs 2 – map zoom, drag points, drag map, get lon x lat

Mapa mundial com zoom scroll, move mapa, adicionar pontos no json e mover, console.log mostrando a latitude e longitude, utilizando D3.js Versão 4 escrita em ES6
Versão com Vuejs v2

HTML + CSS + JS

<!DOCTYPE html>

<head>
    <meta charset="utf-8">
    <script src="https://unpkg.com/vue"></script>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <style>
        body {
            margin: 0;
            position: fixed;
            top: 0;
            right: 0;
            bottom: 0;
            left: 0;
        }
    </style>
</head>

<body>
    <div id="app">
        <div id="map"></div>
    </div>
    <script>
        const cities = [{ 'code': 'OTT', 'city': 'OTTAWA', 'country': 'CANADA', 'lat': '23.10', 'lon': '120.34' }, { 'code': 'BSB', 'city': 'BRASILIA', 'country': 'BRAZIL', 'lat': '-32.85', 'lon': '133.30' }, { 'code': 'DEL', 'city': 'DELHI', 'country': 'INDIA', 'lat': '4.71', 'lon': '-127.57' }, { 'code': 'CMX', 'city': 'CIDADE DO MÉXICO', 'country': 'MÉXICO', 'lat': '0.42', 'lon': '93.19' }, { 'code': 'SID', 'city': 'SIDNEY', 'country': 'AUSTRALIA', 'lat': '-48.38', 'lon': '-71.71' }, { 'code': 'TOK', 'city': 'TOQUIO', 'country': 'JAPÃO', 'lat': '17.34', 'lon': '-81.73' }, { 'code': 'CCA', 'city': 'CIDADE DO CABO', 'country': 'AFRICA DO SUL', 'lat': '-43.20', 'lon': '-171.97' }, { 'code': 'CMP', 'city': 'CAMPO GRANDE', 'country': 'BRASIL', 'lat': '-36.15', 'lon': '130.72' }, { 'code': 'PAR', 'city': 'PARIS', 'country': 'FRANÇA', 'lat': '22.19', 'lon': '174.27' }, { 'code': 'NOY', 'city': 'NOVA YORK', 'country': 'USA', 'lat': '11.23', 'lon': '112.96' }]

        new Vue({
            el: '#app',
            data: {
                background: 'https://upload.wikimedia.org/wikipedia/commons/8/80/World_map_-_low_resolution.svg',
                width: 960,
                height: 600,
                start_x: null,
                start_y: null,
                projection: null,
                scale: 200,
                svg: null,
                path: null,
                g: null,
                drag_handler: null
            },
            methods: {
                createMap() {
                    this.projection = d3.geoMercator()
                        .center([0, 5])
                        .scale(this.scale)
                        .rotate([-180, 0])
                    this.svg = d3.select('#map').append('svg:svg')
                        .attr('width', this.width)
                        .attr('height', this.height)
                    this.path = d3.geoPath()
                        .projection(this.projection),
                        this.g = this.svg.append('g')
                    this.g.append('image')
                        .attr('xlink:href', this.background)
                        .append('path')
                        .attr('d', this.path)

                    this.request()
                },
                request() {
                    const app = this,
                        circles = this.g.selectAll('circle')
                            .data(cities)
                            .enter()
                            .append('a')
                            .attr('xlink:href', d => `https://www.google.com/search?q=${d.city}`
                            )
                            .append('circle')
                            .attr('cx', d => this.projection([d.lon, d.lat])[0])
                            .attr('cy', d => this.projection([d.lon, d.lat])[1])
                            .attr('r', 5)
                            .style('fill', 'red')

                    this.drag_handler = d3.drag()
                        .on('start', this.drag_start)
                        .on('drag', (d, i, a) => this.drag_drag(d, i, a))

                    this.drag_handler(circles)
                },
                drag_start() {
                    this.start_x = +d3.event.x
                    this.start_y = +d3.event.y
                },
                drag_drag(d, i, a) {
                    console.log(
                        'lon x lat', this.projection.invert([d3.event.x, d3.event.y])
                    )
                    d3.select(a[i])
                        .attr('cx', d.x = d3.event.x).attr('cy', d.y = d3.event.y)
                }
            },
            mounted() {
                this.createMap()
                const zoom = d3.zoom()
                    .scaleExtent([0.1, 1]) //zoom limit
                    .on('zoom', () => {
                        this.g.style('stroke-width', `${1.5 / d3.event.transform.k}px`)
                        this.g.attr('transform', d3.event.transform) // updated for d3 v4
                    })

                this.svg.call(zoom)
                    //.call(zoom.transform, d3.zoomIdentity.translate(200, 20).scale(0.25)) //initial size
                    .append('svg:g')
                    .attr('transform', 'translate(100,50) scale(.5,.5)');
            }
        })
    </script>
</body>
</html>

Demo: https://bl.ocks.org/sistemawebpro/41d81ad70d7355691a61a6f7ac7f83d9

D3js v4 map zoom, drag points, drag map, get lon x lat

Mapa mundial com zoom scroll, move mapa, adicionar pontos no json e mover, console.log mostrando a latitude e longitude, utilizando D3.js Versão 4 escrita em ES6

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>
<body>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="script.js"></script>
</body>
</html>

cities.json

[
    {
        "code": "OTT",
        "city": "OTTAWA",
        "country": "CANADA",
        "lat": "23.10",
        "lon": "120.34"
    },
    {
        "code": "BSB",
        "city": "BRASILIA",
        "country": "BRAZIL",
        "lat": "-32.85",
        "lon": "133.30"
    },
    {
        "code": "DEL",
        "city": "DELHI",
        "country": "INDIA",
        "lat": "4.71",
        "lon": "-127.57"
    },
    {
        "code": "CMX",
        "city": "CIDADE DO MÉXICO",
        "country": "MÉXICO",
        "lat": "0.42",
        "lon": "93.19"
    },
    {
        "code": "SID",
        "city": "SIDNEY",
        "country": "AUSTRALIA",
        "lat": "-48.38",
        "lon": "-71.71"
    },
    {
        "code": "TOK",
        "city": "TOQUIO",
        "country": "JAPÃO",
        "lat": "17.34",
        "lon": "-81.73"
    },
    {
        "code": "CCA",
        "city": "CIDADE DO CABO",
        "country": "AFRICA DO SUL",
        "lat": "-43.20",
        "lon": "-171.97"
    },
    {
        "code": "CMP",
        "city": "CAMPO GRANDE",
        "country": "BRASIL",
        "lat": "-36.15",
        "lon": "130.72"
    },
    {
        "code": "PAR",
        "city": "PARIS",
        "country": "FRANÇA",
        "lat": "22.19",
        "lon": "174.27"
    },
    {
        "code": "NOY",
        "city": "NOVA YORK",
        "country": "USA",
        "lat": "11.23",
        "lon": "112.96"
    }
]

script.js Javascript

const width = 960,
    height = 500,
    projection = d3.geoMercator()
        .center([0, 5])
        .scale(200)
        .rotate([-180, 0]),
    svg = d3.select('body').append('svg')
        .attr('width', width)
        .attr('height', height),
    path = d3.geoPath()
        .projection(projection),
    g = svg.append('g')

g.append('image')
    .attr('xlink:href', 'https://upload.wikimedia.org/wikipedia/commons/8/80/World_map_-_low_resolution.svg')
    .append('path')
    .attr('d', path)    

// Carregar cidades | load city
d3.json('cities.json', (error, data) => {
    const circles = g.selectAll('circle')
        .data(data)
        .enter()
        .append('a')
        .attr('xlink:href', d => `https://www.google.com/search?q=${d.city}`
        )
        .append('circle')
        .attr('cx', d => projection([d.lon, d.lat])[0])
        .attr('cy', d => projection([d.lon, d.lat])[1])
        .attr('r', 5)
        .style('fill', 'red'),
        drag_handler = d3.drag()
            .on('start', drag_start)
            .on('drag', drag_drag)

    function drag_start() {
        start_x = +d3.event.x
        start_y = +d3.event.y
    }

    function drag_drag(d) {
        //Get the current scale of the circle 
        //case where we haven't scaled the circle yet
        // get lon x lat
        console.log('lon x lat', projection.invert([d3.event.x, d3.event.y]))

        if (!this.getAttribute('transform'))
            current_scale = 1
        else {
            current_scale_string = this.getAttribute('transform').split(' ')[1]
            current_scale = +current_scale_string.substring(6, current_scale_string.length - 1)
        }
        d3.select(this)
            .attr('cx', d.x = start_x + ((d3.event.x - start_x) / current_scale))
            .attr('cy', d.y = start_y + ((d3.event.y - start_y) / current_scale))
    }

    drag_handler(circles)
})

// zoom and pan
const zoom = d3.zoom()
    .on('zoom', () => {
        g.style('stroke-width', `${1.5 / d3.event.transform.k}px`)
        g.attr('transform', d3.event.transform) // updated for d3 v4
    })

svg.call(zoom)

Demo: https://bl.ocks.org/sistemawebpro/8ef27771795b451d0589e95e5674aa41

D3.js Sankey Plugin – Lista de Presidentes por Mandato, Partido e Estado

Untitled-3

Demostração

Com o arquivo abaixo criei um gráfico listando todos os presidentes do Brasil, contabilizando os Mandatos, Partidos e Estados.

index.html

<!DOCTYPE html>
<html>
<head lang="pt-BR">
    <meta charset="UTF-8">
    <title></title>
    <link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="chart"></div>
<script src="d3.v3.min.js"></script>
<script src="sankey.js"></script>
<script src="custom.js"></script>
</body>
</html>

custom.js

var margin = {top: 1, right: 1, bottom: 6, left: 1},
    width = 1260 - margin.left - margin.right,
    height = 600 - margin.top - margin.bottom;

var formatNumber = d3.format(""),
    format = function(d) { return formatNumber(d).replace('.', '!').replace(',', '.').replace('!', ','); },
    color = d3.scale.category20();
var svg = d3.select("#chart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var sankey = d3.sankey()
    .nodeWidth(100)
    .nodePadding(10)
    .size([width, height]);

var path = sankey.link();

d3.json("arquivo.json", function(energy) {

    var nodeMap = {};
    energy.nodes.forEach(function(x) { nodeMap[x.node] = x; });
    energy.links = energy.links.map(function(x) {
        console.log(x);
        return {
            source: nodeMap[x.source],
            target: nodeMap[x.target],
            value: x.value
        };
    });

    sankey
        .nodes(energy.nodes)
        .links(energy.links)
        .layout(32);

    var link = svg.append("g").selectAll(".link")
        .data(energy.links)
        .enter().append("a")
        .append("path")
        .attr("class", "link")
        .attr("d", path)
        .style("stroke-width", function(d) { return Math.max(1, d.dy); })

        .sort(function(a, b) { return b.dy - a.dy; });

    link.append("title")
        .text(function(d) { return d.source.name + " → " + d.target.name + "\n" + format(d.value).replace('.', '!').replace(',', '.').replace('!', ','); });

    var node = svg.append("g").selectAll(".node")
        .data(energy.nodes)
        .enter().append("g")
        .append("a")
        .attr("class", "node")
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })

        .call(d3.behavior.drag()
            .origin(function(d) { return d; })
            .on("dragstart", function() { this.parentNode.appendChild(this); })
            .on("drag", dragmove))
        .on("mouseover", fade(0.2))
        .on("mouseout", fade(1));

    node.append("rect")
        .attr("height", function(d) { return d.dy; })


        .attr("width", sankey.nodeWidth())
        .style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); })
        .style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
        .append("title")
        .text(function (d) {
            var titleText = d.name + " - " +
                format(d.value) + " total" + "\n" + "\n";
            var sourcesText = "";
            d.targetLinks.forEach(function (dstLnk) {
                sourcesText += "a partir de " + dstLnk.source.name + " - " +
                    format(dstLnk.value) + "\n";
            });
            return titleText + sourcesText;
        });

    node.append("text")
        .attr("class","nodeValue")
        .text(function(d) { return d.name + "\n" + format(d.value).replace('.', '!').replace(',', '.').replace('!', ','); });

    ///align vertically???
    node.selectAll("text.nodeValue")
        .attr("x", sankey.nodeWidth() / 2)
        .attr("y", function (d) { return (d.dy / 2) })
        .text(function (d) { return formatNumber(d.value).replace('.', '!').replace(',', '.').replace('!', ','); })
        .attr("dy", 5)
        .attr("text-anchor", "middle");

    node.append("text")
        .attr("class","nodeLabel");

    node.selectAll("text.nodeLabel")
        .attr("x", -6)
        .attr("y", function(d) { return d.dy / 2; })
        .attr("dy", ".35em")
        .attr("text-anchor", "end")
        .attr("transform", null)
        .text(function(d) { return d.name; })
        .filter(function(d) { return d.x < width / 2; })
        .attr("class", function (d) { if (d.categoria === 1) return "titulo"; })
        .attr("x", 6 + sankey.nodeWidth())
        .attr("text-anchor", "start");

    function dragmove(d) {
        d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")");
        sankey.relayout();
        link.attr("d", path);
    }
    function fade(opacity) {
        return function (g, i) {
            var elements = svg.selectAll(".node");
            elements = elements.filter(function (d) { return d.node != energy.nodes[i].node });
            elements.transition()
                .style("opacity", opacity);
            svg.selectAll(".link")
                .filter(function (d) { return d.source.node != energy.nodes[i].node && d.target.node != energy.nodes[i].node  })
                .transition()
                .style("opacity", opacity);
        };
    }
});

arquivo.json

{
  "nodes": [
    {
      "name": "Marechal Deodoro da Fonseca",
      "node": 0,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Floriano Peixoto",
      "node": 1,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Prudente de Morais",
      "node": 2,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Campos Sales",
      "node": 3,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Rodrigues Alves",
      "node": 4,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Afonso Pena",
      "node": 5,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Nilo Peçanha",
      "node": 6,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Hermes da Fonseca",
      "node": 7,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Venceslau Brás",
      "node": 8,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Delfim Moreira",
      "node": 9,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Epitácio Pessoa",
      "node": 10,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Artur Bernardes",
      "node": 11,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Washington Luís",
      "node": 12,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Getúlio Vargas",
      "node": 13,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "José Linhares",
      "node": 14,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Eurico Gaspar Dutra",
      "node": 15,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Café Filho",
      "node": 16,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Carlos Luz",
      "node": 17,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Nereu Ramos",
      "node": 18,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Juscelino Kubitschek",
      "node": 19,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Jânio Quadros",
      "node": 20,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Ranieri Mazzilli",
      "node": 21,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "João Goulart",
      "node": 22,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Castelo Branco",
      "node": 23,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Costa e Silva",
      "node": 24,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Emilio Medici",
      "node": 25,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Ernesto Geisel",
      "node": 26,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "João Figueiredo",
      "node": 27,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "José Sarney",
      "node": 28,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Fernando Collor",
      "node": 29,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Itamar Franco",
      "node": 30,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Fernando Henrique Cardoso",
      "node": 31,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Luís Inácio Lula da Silva",
      "node": 32,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "Dilma Rousseff",
      "node": 33,
      "categoria": 0,
      "xPos": 0
    },
    {
      "name": "militar",
      "node": 34,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "Partido Republicano Federal",
      "node": 35,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "Partido Republicano Paulista",
      "node": 36,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "Partido Republicano Fluminense",
      "node": 37,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "Partido Republicano Conservador",
      "node": 38,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "Partido Republicano Mineiro",
      "node": 39,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "Aliança Liberal",
      "node": 40,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "nenhum",
      "node": 41,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "PSP",
      "node": 42,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "PTN",
      "node": 43,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "PTB",
      "node": 44,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "PSD",
      "node": 45,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "ARENA (militar)",
      "node": 46,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "PDS (militar)",
      "node": 47,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "PRN",
      "node": 48,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "PMDB",
      "node": 49,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "PSDB",
      "node": 50,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "PT",
      "node": 51,
      "categoria": 0,
      "xPos": 1
    },
    {
      "name": "Alagoas",
      "node": 52,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Paraíba",
      "node": 53,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Mato Grosso",
      "node": 54,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Rio Grande do Norte",
      "node": 55,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Santa Catarina",
      "node": 56,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Mato Grosso do Sul",
      "node": 57,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "São Paulo",
      "node": 58,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Ceará",
      "node": 59,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Rio Grande do Sul",
      "node": 60,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Maranhão",
      "node": 61,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Rio de Janeiro",
      "node": 62,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Pernambuco",
      "node": 63,
      "categoria": 0,
      "xPos": 2
    },
    {
      "name": "Minas Gerais",
      "node": 64,
      "categoria": 0,
      "xPos": 2
    }
  ],
  "links": [
    {
      "source": 0,
      "target": 34,
      "value": 1
    },
    {
      "source": 1,
      "target": 34,
      "value": 1
    },
    {
      "source": 2,
      "target": 35,
      "value": 1
    },
    {
      "source": 3,
      "target": 36,
      "value": 1
    },
    {
      "source": 4,
      "target": 36,
      "value": 1
    },
    {
      "source": 5,
      "target": 39,
      "value": 1
    },
    {
      "source": 6,
      "target": 37,
      "value": 1
    },
    {
      "source": 7,
      "target": 38,
      "value": 1
    },
    {
      "source": 8,
      "target": 39,
      "value": 1
    },
    {
      "source": 9,
      "target": 39,
      "value": 1
    },
    {
      "source": 10,
      "target": 39,
      "value": 1
    },
    {
      "source": 11,
      "target": 39,
      "value": 1
    },
    {
      "source": 12,
      "target": 36,
      "value": 1
    },
    {
      "source": 13,
      "target": 40,
      "value": 1
    },
    {
      "source": 14,
      "target": 41,
      "value": 1
    },
    {
      "source": 15,
      "target": 45,
      "value": 1
    },
    {
      "source": 13,
      "target": 44,
      "value": 1
    },
    {
      "source": 16,
      "target": 42,
      "value": 1
    },
    {
      "source": 17,
      "target": 45,
      "value": 1
    },
    {
      "source": 18,
      "target": 45,
      "value": 1
    },
    {
      "source": 19,
      "target": 45,
      "value": 1
    },
    {
      "source": 20,
      "target": 43,
      "value": 1
    },
    {
      "source": 21,
      "target": 45,
      "value": 1
    },
    {
      "source": 22,
      "target": 44,
      "value": 1
    },
    {
      "source": 21,
      "target": 45,
      "value": 1
    },
    {
      "source": 23,
      "target": 46,
      "value": 1
    },
    {
      "source": 24,
      "target": 46,
      "value": 1
    },
    {
      "source": 25,
      "target": 46,
      "value": 1
    },
    {
      "source": 26,
      "target": 46,
      "value": 1
    },
    {
      "source": 27,
      "target": 47,
      "value": 1
    },
    {
      "source": 28,
      "target": 49,
      "value": 1
    },
    {
      "source": 29,
      "target": 48,
      "value": 1
    },
    {
      "source": 30,
      "target": 49,
      "value": 1
    },
    {
      "source": 30,
      "target": 49,
      "value": 1
    },
    {
      "source": 31,
      "target": 50,
      "value": 1
    },
    {
      "source": 31,
      "target": 50,
      "value": 1
    },
    {
      "source": 32,
      "target": 51,
      "value": 1
    },
    {
      "source": 32,
      "target": 51,
      "value": 1
    },
    {
      "source": 33,
      "target": 51,
      "value": 1
    },
    {
      "source": 33,
      "target": 51,
      "value": 1
    },
    {
      "source": 34,
      "target": 52,
      "value": 1
    },
    {
      "source": 34,
      "target": 52,
      "value": 1
    },
    {
      "source": 35,
      "target": 58,
      "value": 1
    },
    {
      "source": 36,
      "target": 58,
      "value": 1
    },
    {
      "source": 36,
      "target": 58,
      "value": 1
    },
    {
      "source": 39,
      "target": 64,
      "value": 1
    },
    {
      "source": 37,
      "target": 62,
      "value": 1
    },
    {
      "source": 38,
      "target": 60,
      "value": 1
    },
    {
      "source": 39,
      "target": 64,
      "value": 1
    },
    {
      "source": 39,
      "target": 64,
      "value": 1
    },
    {
      "source": 39,
      "target": 53,
      "value": 1
    },
    {
      "source": 39,
      "target": 64,
      "value": 1
    },
    {
      "source": 36,
      "target": 62,
      "value": 1
    },
    {
      "source": 40,
      "target": 60,
      "value": 1
    },
    {
      "source": 41,
      "target": 59,
      "value": 1
    },
    {
      "source": 45,
      "target": 54,
      "value": 1
    },
    {
      "source": 44,
      "target": 60,
      "value": 1
    },
    {
      "source": 42,
      "target": 55,
      "value": 1
    },
    {
      "source": 45,
      "target": 64,
      "value": 1
    },
    {
      "source": 45,
      "target": 56,
      "value": 1
    },
    {
      "source": 45,
      "target": 64,
      "value": 1
    },
    {
      "source": 43,
      "target": 57,
      "value": 1
    },
    {
      "source": 45,
      "target": 58,
      "value": 1
    },
    {
      "source": 44,
      "target": 60,
      "value": 1
    },
    {
      "source": 45,
      "target": 58,
      "value": 1
    },
    {
      "source": 46,
      "target": 59,
      "value": 1
    },
    {
      "source": 46,
      "target": 60,
      "value": 1
    },
    {
      "source": 46,
      "target": 60,
      "value": 1
    },
    {
      "source": 46,
      "target": 60,
      "value": 1
    },
    {
      "source": 47,
      "target": 62,
      "value": 1
    },
    {
      "source": 49,
      "target": 61,
      "value": 1
    },
    {
      "source": 48,
      "target": 62,
      "value": 1
    },
    {
      "source": 49,
      "target": 64,
      "value": 1
    },
    {
      "source": 49,
      "target": 64,
      "value": 1
    },
    {
      "source": 50,
      "target": 62,
      "value": 1
    },
    {
      "source": 50,
      "target": 62,
      "value": 1
    },
    {
      "source": 51,
      "target": 63,
      "value": 1
    },
    {
      "source": 51,
      "target": 64,
      "value": 1
    }
  ]
}

D3.js Sankey Diagrama com posição por coluna fixada dinamicamente pelo json

Untitled-2

Foi modificado o plugin D3 Sankey plugin para atender o formato de gráfico que queria utiliza-lo, para este exemplo vou disponibilizar os códigos.

index.html

<!DOCTYPE html>
<html>
<head lang="pt-BR">
    <meta charset="UTF-8">
    <title></title>
    <link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="chart"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="sankey.js"></script>
<script src="custom.js"></script>
</body>
</html>

sankey.js (Modificado)

d3.sankey = function() {
    var sankey = {},
        nodeWidth = 24,
        nodePadding = 8,
        size = [1, 1],
        nodes = [],
        links = [];

    sankey.nodeWidth = function(_) {
        if (!arguments.length) return nodeWidth;
        nodeWidth = +_;
        return sankey;
    };

    sankey.nodePadding = function(_) {
        if (!arguments.length) return nodePadding;
        nodePadding = +_;
        return sankey;
    };

    sankey.nodes = function(_) {
        if (!arguments.length) return nodes;
        nodes = _;
        return sankey;
    };

    sankey.links = function(_) {
        if (!arguments.length) return links;
        links = _;
        return sankey;
    };

    sankey.size = function(_) {
        if (!arguments.length) return size;
        size = _;
        return sankey;
    };

    sankey.layout = function(iterations) {
        computeNodeLinks();
        computeNodeValues();
        computeNodeBreadths();
        computeNodeDepths(iterations);
        computeLinkDepths();
        return sankey;
    };

    sankey.relayout = function() {
        computeLinkDepths();
        return sankey;
    };

    sankey.link = function() {
        var curvature = .5;

        function link(d) {
            var x0 = d.source.x + d.source.dx,
                x1 = d.target.x,
                xi = d3.interpolateNumber(x0, x1),
                x2 = xi(curvature),
                x3 = xi(1 - curvature),
                y0 = d.source.y + d.sy + d.dy / 2,
                y1 = d.target.y + d.ty + d.dy / 2;
            return "M" + x0 + "," + y0
                + "C" + x2 + "," + y0
                + " " + x3 + "," + y1
                + " " + x1 + "," + y1;
        }

        link.curvature = function(_) {
            if (!arguments.length) return curvature;
            curvature = +_;
            return link;
        };

        return link;
    };

    // Populate the sourceLinks and targetLinks for each node.
    // Also, if the source and target are not objects, assume they are indices.
    function computeNodeLinks() {
        nodes.forEach(function(node) {
            node.sourceLinks = [];
            node.targetLinks = [];
        });
        links.forEach(function(link) {
            var source = link.source,
                target = link.target;
            if (typeof source === "number") source = link.source = nodes[link.source];
            if (typeof target === "number") target = link.target = nodes[link.target];
            source.sourceLinks.push(link);
            target.targetLinks.push(link);
        });
    }

    // Compute the value (size) of each node by summing the associated links.
    function computeNodeValues() {
        nodes.forEach(function(node) {
            node.value = Math.max(
                d3.sum(node.sourceLinks, value),
                d3.sum(node.targetLinks, value)
            );
        });
    }

    // Iteratively assign the breadth (x-position) for each node.
    // Nodes are assigned the maximum breadth of incoming neighbors plus one;
    // nodes with no incoming links are assigned breadth zero, while
    // nodes with no outgoing links are assigned the maximum breadth.
    function computeNodeBreadths() {
        var remainingNodes = nodes,
            nextNodes,
            x = 0;

        while (remainingNodes.length) {
            nextNodes = [];
            remainingNodes.forEach(function(node) {

                if (node.xPos) {
                    node.x = node.xPos;
                } else {
                    node.x = x;
                }

                node.dx = nodeWidth;
                node.sourceLinks.forEach(function(link) {
                    nextNodes.push(link.target);
                });
            });
            remainingNodes = nextNodes;
            ++x;
        }

        //
        moveSinksRight(x);
        scaleNodeBreadths((width - nodeWidth) / (x - 1));

    }


    function moveSinksRight(x) {
        nodes.forEach(function(node) {
            if (!node.sourceLinks.length) {
                //node.x = x - 1;
                node.x = node.xPos;
            }
        });
    }

    function scaleNodeBreadths(kx) {
        nodes.forEach(function(node) {
            node.x *= kx;
        });
    }

    function computeNodeDepths(iterations) {
        var nodesByBreadth = d3.nest()
            .key(function(d) { return d.x; })
            .sortKeys(d3.ascending)
            .entries(nodes)
            .map(function(d) { return d.values; });

        //
        initializeNodeDepth();
        resolveCollisions();
        for (var alpha = 1; iterations > 0; --iterations) {
            relaxRightToLeft(alpha *= .99);
            resolveCollisions();
            relaxLeftToRight(alpha);
            resolveCollisions();
        }

        function initializeNodeDepth() {
            var ky = d3.min(nodesByBreadth, function(nodes) {
                return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value);
            });

            nodesByBreadth.forEach(function(nodes) {
                nodes.forEach(function(node, i) {
                    node.y = i;
                    node.dy = node.value * ky;
                });
            });

            links.forEach(function(link) {
                link.dy = link.value * ky;
            });
        }

        function relaxLeftToRight(alpha) {
            nodesByBreadth.forEach(function(nodes, breadth) {
                nodes.forEach(function(node) {
                    if (node.targetLinks.length) {
                        var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value);
                        node.y += (y - center(node)) * alpha;
                    }
                });
            });

            function weightedSource(link) {
                return center(link.source) * link.value;
            }
        }

        function relaxRightToLeft(alpha) {
            nodesByBreadth.slice().reverse().forEach(function(nodes) {
                nodes.forEach(function(node) {
                    if (node.sourceLinks.length) {
                        var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value);
                        node.y += (y - center(node)) * alpha;
                    }
                });
            });

            function weightedTarget(link) {
                return center(link.target) * link.value;
            }
        }

        function resolveCollisions() {
            nodesByBreadth.forEach(function(nodes) {
                var node,
                    dy,
                    y0 = 0,
                    n = nodes.length,
                    i;

                // Push any overlapping nodes down.
                nodes.sort(ascendingDepth);
                for (i = 0; i < n; ++i) {
                    node = nodes[i];
                    dy = y0 - node.y;
                    if (dy > 0) node.y += dy;
                    y0 = node.y + node.dy + nodePadding;
                }

                // If the bottommost node goes outside the bounds, push it back up.
                dy = y0 - nodePadding - size[1];
                if (dy > 0) {
                    y0 = node.y -= dy;

                    // Push any overlapping nodes back up.
                    for (i = n - 2; i >= 0; --i) {
                        node = nodes[i];
                        dy = node.y + node.dy + nodePadding - y0;
                        if (dy > 0) node.y -= dy;
                        y0 = node.y;
                    }
                }
            });
        }

        function ascendingDepth(a, b) {
            return a.y - b.y;
        }
    }

    function computeLinkDepths() {
        nodes.forEach(function(node) {
            node.sourceLinks.sort(ascendingTargetDepth);
            node.targetLinks.sort(ascendingSourceDepth);
        });
        nodes.forEach(function(node) {
            var sy = 0, ty = 0;
            node.sourceLinks.forEach(function(link) {
                link.sy = sy;
                sy += link.dy;
            });
            node.targetLinks.forEach(function(link) {
                link.ty = ty;
                ty += link.dy;
            });
        });

        function ascendingSourceDepth(a, b) {
            return a.source.y - b.source.y;
        }

        function ascendingTargetDepth(a, b) {
            return a.target.y - b.target.y;
        }
    }

    function center(node) {
        return node.y + node.dy / 2;
    }

    function value(link) {
        return link.value;
    }

    return sankey;
};

custom.js (modificado)

var margin = {top: 1, right: 1, bottom: 6, left: 1},
    width = 1260 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var formatNumber = d3.format(",.2f"),
    format = function(d) { return formatNumber(d).replace('.', '!').replace(',', '.').replace('!', ','); },
    color = d3.scale.category20();
var svg = d3.select("#chart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var sankey = d3.sankey()
    .nodeWidth(100)
    .nodePadding(10)
    .size([width, height]);

var path = sankey.link();

d3.json("package.json", function(energy) {

    var nodeMap = {};
    energy.nodes.forEach(function(x) { nodeMap[x.node] = x; });
    energy.links = energy.links.map(function(x) {
        console.log(x);
        return {
            source: nodeMap[x.source],
            target: nodeMap[x.target],
            value: x.value
        };
    });

    sankey
        .nodes(energy.nodes)
        .links(energy.links)
        .layout(32);

    var link = svg.append("g").selectAll(".link")
        .data(energy.links)
        .enter().append("path")
        .attr("class", "link")
        .attr("d", path)
        .style("stroke-width", function(d) { return Math.max(1, d.dy); })
        .sort(function(a, b) { return b.dy - a.dy; });

    link.append("title")
        .text(function(d) { return d.source.name + " → " + d.target.name + "\n" + format(d.value).replace('.', '!').replace(',', '.').replace('!', ','); });

    var node = svg.append("g").selectAll(".node")
        .data(energy.nodes)
        .enter().append("g")
        .attr("class", "node")
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
        .call(d3.behavior.drag()
            .origin(function(d) { return d; })
            .on("dragstart", function() { this.parentNode.appendChild(this); })
            .on("drag", dragmove));

    node.append("rect")
        .attr("height", function(d) { return d.dy; })
        .attr("width", sankey.nodeWidth())
        .style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); })
        .style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
        .append("title")
        .text(function (d) {
            var titleText = d.name + " - " +
                format(d.value) + " total" + "\n" + "\n";
            var sourcesText = "";
            d.targetLinks.forEach(function (dstLnk) {
                sourcesText += "a partir de " + dstLnk.source.name + " - " +
                    format(dstLnk.value) + "\n";
            });
            return titleText + sourcesText;
        });

    node.append("text")
        .attr("class","nodeValue")
        .text(function(d) { return d.name + "\n" + format(d.value).replace('.', '!').replace(',', '.').replace('!', ','); });

    ///align vertically???
    node.selectAll("text.nodeValue")
        .attr("x", sankey.nodeWidth() / 2)
        .attr("y", function (d) { return (d.dy / 2) })
        .text(function (d) { return formatNumber(d.value).replace('.', '!').replace(',', '.').replace('!', ','); })
        .attr("dy", 5)
        .attr("text-anchor", "middle");

    node.append("text")
        .attr("class","nodeLabel");

    node.selectAll("text.nodeLabel")
        .attr("x", -6)
        .attr("y", function(d) { return d.dy / 2; })
        .attr("dy", ".35em")
        .attr("text-anchor", "end")
        .attr("transform", null)
        .text(function(d) { return d.name; })
        .filter(function(d) { return d.x < width / 2; })
        .attr("class", function (d) { if (d.categoria === 1) return "titulo"; })
        .attr("x", 6 + sankey.nodeWidth())
        .attr("text-anchor", "start");

    function dragmove(d) {
        d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")");
        sankey.relayout();
        link.attr("d", path);
    }
});

package.json

{
  "nodes": [
    {
      "node": 0,
      "name": "MERCEARIA",
      "categoria": 1,
      "xPos": 0
    },
    {
      "node": 5,
      "name": "MERCEARIA",
      "categoria": 1,
      "xPos": 3
    },
    {
      "node": 201,
      "name": "MERC SECA SALG",
      "categoria": 0,
      "xPos": 1
    },
    {
      "node": 202,
      "name": "MERC SECA DOCE",
      "categoria": 0,
      "xPos": 1
    },
    {
      "node": 203,
      "name": "LIQUIDA",
      "categoria": 0,
      "xPos": 1
    },
    {
      "node": 204,
      "name": "PERFUMARIAS",
      "categoria": 0,
      "xPos": 1
    },
    {
      "node": 205,
      "name": "LIMPEZA",
      "categoria": 0,
      "xPos": 1
    },
    {
      "node": 226,
      "name": "CONVENIENCIA",
      "categoria": 0,
      "xPos": 1
    },
    {
      "node": 32,
      "name": "32-ALIMENT.COMPLEMENTAR",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 31,
      "name": "31-ALIMENTAÇÃO BÁSICA",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 39,
      "name": "39-PEIXARIA",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 42,
      "name": "42-PERECIVEIS COMPLEMENT",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 38,
      "name": "38-FLV",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 36,
      "name": "36-FOOD SERVICE",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 99,
      "name": "Sem Classificação Comercial",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 33,
      "name": "33-LÍQUIDA",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 43,
      "name": "43-PADARIA",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 47,
      "name": "47-CASA",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 41,
      "name": "41-LATICINIOS",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 34,
      "name": "34-PERFUMARIA",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 49,
      "name": "49-LAZER",
      "categoria": 0,
      "xPos": 2
    },
    {
      "node": 35,
      "name": "35-LIMPEZA",
      "categoria": 0,
      "xPos": 2
    }
  ],
  "links": [
    {
      "source": 0,
      "target": 201,
      "value": 2.78
    },
    {
      "source": 201,
      "target": 35,
      "value": 0.01
    },
    {
      "source": 35,
      "target": 5,
      "value": 0.01
    },

    {
      "source": 0,
      "target": 202,
      "value": 35
    },
    {
      "source": 0,
      "target": 203,
      "value": 54.90
    },
    {
      "source": 0,
      "target": 204,
      "value": 122.64
    },
    {
      "source": 0,
      "target": 205,
      "value": 23.96
    },
    {
      "source": 0,
      "target": 226,
      "value": 32.90
    },
    {
      "source": 32,
      "target": 5,
      "value": 2.78
    },
    {
      "source": 31,
      "target": 5,
      "value": 3.56
    },
    {
      "source": 39,
      "target": 5,
      "value": 5.89
    },
    {
      "source": 42,
      "target": 5,
      "value": 6.48
    },
    {
      "source": 38,
      "target": 5,
      "value": 15.75
    },
    {
      "source": 36,
      "target": 5,
      "value": 139.75
    },
    {
      "source": 99,
      "target": 5,
      "value": 26.90
    },
    {
      "source": 33,
      "target": 5,
      "value": 23
    },
    {
      "source": 43,
      "target": 5,
      "value": 5.47
    },
    {
      "source": 47,
      "target": 5,
      "value": 23.92
    },
    {
      "source": 41,
      "target": 5,
      "value": 62.40
    },
    {
      "source": 34,
      "target": 5,
      "value": 122.64
    },
    {
      "source": 49,
      "target": 5,
      "value": 4.29
    },
    {
      "source": 35,
      "target": 5,
      "value": 23.96
    }
  ]
}

style.css

body{
    font-family: "Arial";
}
#chart {
    height: 500px;
}

.node rect {
    cursor: move;
    fill-opacity: .9;
    shape-rendering: crispEdges;
}

.node text {
    pointer-events: none;
    text-shadow: 0 1px 0 #fff;
    font-size: 12px;
}

text.nodeLabel {
    font-size: 10px;
}

.link {
    fill: none;
    stroke: #000;
    stroke-opacity: .2;
}

.link:hover {
    stroke-opacity: .5;
}
.node text.titulo{
    font-weight: bold;
    font-size: 14px;
}

JointJS – Biblioteca para criação de diagramas JS

jointjs

Surgiu-se a necessidade de criação de diagramas, fui a pesquisa para encontrar alguma ferramenta para auxiliar, como existe vários prontos não é necessário criar a roda.

Encontrei diversos plugins mas nenhum havia me chamado atenção, uma que gostei muito foi o D3.js mas achei muito complicado sui manipulação, muito trabalhoso pra criar exatamente o que queria.

Das que encontrei, me chamaram muito atenção esses plugin:
GoJS – Infelizmente é paga
img_551d33df0354a

JsPlumb – Grátis
img_551d34ebec5c3

mermaid – Grátis
header

A que mais gostei foi JointJS com esse plugin é possível criar diagramas interessante, de um forma simples e poderosa.

A instalação super simples, necessário:

<link rel="stylesheet" type="text/css" href="joint.css" />
<script src="jquery.min.js"></script>
<script src="lodash.min.js"></script>
<script src="backbone-min.js"></script>
<script src="joint.js"></script>

jQuery: 2.0.3
Lodash: 3.10.1
Backbone: 1.2.1

Development version (0.9.5)
joint.js joint.css

Production version (0.9.5)
joint.min.js joint.min.css

Algumas imagens das possibilidades do JointJS.
ivr

traceroutes

rappid-new-showcase

rappid_attack_designer