PageSourceSearch

https://riversentiment.app/src/js/vis.js

js riversentiment.app collected 2026-10-06 22:45:31 UTC 26,841 bytes, 800 lines download raw bytes

1/**
2 * 
3 * @returns D3 Sankey SVG
4 */
5 d3.sankey = function() {
6    var sankey = {},
7        nodeWidth = 24,
8        nodePadding = 8,
9        size = [1, 1],
10        nodes = [],
11        links = [];
12  
13    sankey.nodeWidth = function(_) {
14      if (!arguments.length) return nodeWidth;
15      nodeWidth = +_;
16      return sankey;
17    };
18  
19    sankey.nodePadding = function(_) {
20      if (!arguments.length) return nodePadding;
21      nodePadding = +_;
22      return sankey;
23    };
24  
25    sankey.nodes = function(_) {
26      if (!arguments.length) return nodes;
27      nodes = _;
28      return sankey;
29    };
30  
31    sankey.links = function(_) {
32      if (!arguments.length) return links;
33      links = _;
34      return sankey;
35    };
36  
37    sankey.size = function(_) {
38      if (!arguments.length) return size;
39      size = _;
40      return sankey;
41    };
42  
43    sankey.layout = function(iterations) {
44      computeNodeLinks();
45      computeNodeValues();
46      computeNodeBreadths();
47      computeNodeDepths(iterations);
48      computeLinkDepths();
49      return sankey;
50    };
51  
52    sankey.relayout = function() {
53      computeLinkDepths();
54      return sankey;
55    };
56  
57    sankey.link = function() {
58      var curvature = .5;
59  
60      function link(d) {
61        var x0 = d.source.x + d.source.dx,
62            x1 = d.target.x,
63            xi = d3.interpolateNumber(x0, x1),
64            x2 = xi(curvature),
65            x3 = xi(1 - curvature),
66            y0 = d.source.y + d.sy + d.dy / 2,
67            y1 = d.target.y + d.ty + d.dy / 2;
68        return "M" + x0 + "," + y0
69             + "C" + x2 + "," + y0
70             + " " + x3 + "," + y1
71             + " " + x1 + "," + y1;
72      }
73  
74      link.curvature = function(_) {
75        if (!arguments.length) return curvature;
76        curvature = +_;
77        return link;
78      };
79  
80      return link;
81    };
82  
83    // Populate the sourceLinks and targetLinks for each node.
84    // Also, if the source and target are not objects, assume they are indices.
85    function computeNodeLinks() {
86      nodes.forEach(function(node) {
87        node.sourceLinks = [];
88        node.targetLinks = [];
89      });
90      links.forEach(function(link) {
91        var source = link.source,
92            target = link.target;
93        if (typeof source === "number") source = link.source = nodes[link.source];
94        if (typeof target === "number") target = link.target = nodes[link.target];
95        source.sourceLinks.push(link);
96        target.targetLinks.push(link);
97      });
98    }
99  
100    // Compute the value (size) of each node by summing the associated links.
101    function computeNodeValues() {
102      nodes.forEach(function(node) {
103        node.value = Math.max(
104          d3.sum(node.sourceLinks, value),
105          d3.sum(node.targetLinks, value)
106        );
107      });
108    }
109  
110    // Iteratively assign the breadth (x-position) for each node.
111    // Nodes are assigned the maximum breadth of incoming neighbors plus one;
112    // nodes with no incoming links are assigned breadth zero, while
113    // nodes with no outgoing links are assigned the maximum breadth.
114    function computeNodeBreadths() {
115      var remainingNodes = nodes,
116          nextNodes,
117          x = 0;
118  
119      while (remainingNodes.length) {
120        nextNodes = [];
121        remainingNodes.forEach(function(node) {
122          node.x = x;
123          node.dx = nodeWidth;
124          node.sourceLinks.forEach(function(link) {
125            if (nextNodes.indexOf(link.target) < 0) {
126              nextNodes.push(link.target);
127            }
128          });
129        });
130        remainingNodes = nextNodes;
131        ++x;
132      }
133  
134      //
135      moveSinksRight(x);
136      scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
137    }
138  
139    function moveSourcesRight() {
140      nodes.forEach(function(node) {
141        if (!node.targetLinks.length) {
142          node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1;
143        }
144      });
145    }
146  
147    function moveSinksRight(x) {
148      nodes.forEach(function(node) {
149        if (!node.sourceLinks.length) {
150          node.x = x - 1;
151        }
152      });
153    }
154  
155    function scaleNodeBreadths(kx) {
156      nodes.forEach(function(node) {
157        node.x *= kx;
158      });
159    }
160  
161    function computeNodeDepths(iterations) {
162      var nodesByBreadth = d3.nest()
163          .key(function(d) { return d.x; })
164          .sortKeys(d3.ascending)
165          .entries(nodes)
166          .map(function(d) { return d.values; });
167  
168      //
169      initializeNodeDepth();
170      resolveCollisions();
171      for (var alpha = 1; iterations > 0; --iterations) {
172        relaxRightToLeft(alpha *= .99);
173        resolveCollisions();
174        relaxLeftToRight(alpha);
175        resolveCollisions();
176      }
177  
178      function initializeNodeDepth() {
179        var ky = d3.min(nodesByBreadth, function(nodes) {
180          return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value);
181        });
182  
183        nodesByBreadth.forEach(function(nodes) {
184          nodes.forEach(function(node, i) {
185            node.y = i;
186            node.dy = node.value * ky;
187          });
188        });
189  
190        links.forEach(function(link) {
191          link.dy = link.value * ky;
192        });
193      }
194  
195      function relaxLeftToRight(alpha) {
196        nodesByBreadth.forEach(function(nodes, breadth) {
197          nodes.forEach(function(node) {
198            if (node.targetLinks.length) {
199              var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value);
200              node.y += (y - center(node)) * alpha;
201            }
202          });
203        });
204  
205        function weightedSource(link) {
206          return center(link.source) * link.value;
207        }
208      }
209  
210      function relaxRightToLeft(alpha) {
211        nodesByBreadth.slice().reverse().forEach(function(nodes) {
212          nodes.forEach(function(node) {
213            if (node.sourceLinks.length) {
214              var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value);
215              node.y += (y - center(node)) * alpha;
216            }
217          });
218        });
219  
220        function weightedTarget(link) {
221          return center(link.target) * link.value;
222        }
223      }
224  
225      function resolveCollisions() {
226        nodesByBreadth.forEach(function(nodes) {
227          var node,
228              dy,
229              y0 = 0,
230              n = nodes.length,
231              i;
232  
233          // Push any overlapping nodes down.
234          nodes.sort(ascendingDepth);
235          for (i = 0; i < n; ++i) {
236            node = nodes[i];
237            dy = y0 - node.y;
238            if (dy > 0) node.y += dy;
239            y0 = node.y + node.dy + nodePadding;
240          }
241  
242          // If the bottommost node goes outside the bounds, push it back up.
243          dy = y0 - nodePadding - size[1];
244          if (dy > 0) {
245            y0 = node.y -= dy;
246  
247            // Push any overlapping nodes back up.
248            for (i = n - 2; i >= 0; --i) {
249              node = nodes[i];
250              dy = node.y + node.dy + nodePadding - y0;
251              if (dy > 0) node.y -= dy;
252              y0 = node.y;
253            }
254          }
255        });
256      }
257  
258      function ascendingDepth(a, b) {
259        return a.y - b.y;
260      }
261    }
262  
263    function computeLinkDepths() {
264      nodes.forEach(function(node) {
265        node.sourceLinks.sort(ascendingTargetDepth);
266        node.targetLinks.sort(ascendingSourceDepth);
267      });
268      nodes.forEach(function(node) {
269        var sy = 0, ty = 0;
270        node.sourceLinks.forEach(function(link) {
271          link.sy = sy;
272          sy += link.dy;
273        });
274        node.targetLinks.forEach(function(link) {
275          link.ty = ty;
276          ty += link.dy;
277        });
278      });
279  
280      function ascendingSourceDepth(a, b) {
281        return a.source.y - b.source.y;
282      }
283  
284      function ascendingTargetDepth(a, b) {
285        return a.target.y - b.target.y;
286      }
287    }
288  
289    function center(node) {
290      return node.y + node.dy / 2;
291    }
292  
293    function value(link) {
294      return link.value;
295    }
296  
297    return sankey;
298  };
299
300/**
301 * Generate charts based on github open data
302 * @param {string} mean_senti_path 
303 * @param {string} eco 
304 * @param {string} emo_freq_path 
305 * @param {string} nounphrase_path 
306 * @param {string} rnag_path 
307 */
308
309function chart(mean_senti_path, eco, emo_freq_path, nounphrase_path,rnag_path) {
310
311
312    /**
313     * 
314     * Ecological status csv
315     * 
316     */
317    var tabulate = function (data, columns) {
318        var table = d3.select('#csv').append('table')
319        var thead = table.append('thead')
320        var tbody = table.append('tbody')
321
322        thead.append('tr')
323            .selectAll('th')
324            .data(columns)
325            .enter()
326            .append('th')
327            .text(function (d) {
328                return d
329            })
330
331        var rows = tbody.selectAll('tr')
332            .data(data)
333            .enter()
334            .append('tr')
335
336        var cells = rows.selectAll('td')
337            .data(function (row) {
338                return columns.map(function (column) {
339                    return {
340                        column: column,
341                        value: row[column]
342                    }
343                })
344            })
345            .enter()
346            .append('td')
347            .text(function (d) {
348                return d.value
349            })
350            .style('background-color', function (d) {
351                if (d.value == "Poor") {
352                    return 'orange';
353                } else if (d.value == "Moderate") {
354                    return 'yellow';
355                } else if (d.value == "Good") {
356                    return 'green';
357                } else {
358                    return 'red';
359                }
360            });
361
362        return table;
363    }
364
365    d3.csv(eco, function (data) {
366        var columns = ['2013', '2014', '2015', '2016', '2019']
367        tabulate(data, columns)
368    })
369
370    /**
371     * 
372     * Sentiment Polarity Score
373     * 
374     */
375
376    // set the dimensions and margins of the graph
377    var margin0 = {
378            top: 10,
379            right: 30,
380            bottom: 30,
381            left: 60
382        },
383        width0 = 1200 - margin0.left - margin0.right,
384        height0 = 400 - margin0.top - margin0.bottom;
385
386    // append the svg object to the body of the page
387    var svg0 = d3.select("#my_dataviz")
388        .append("svg")
389        .attr("width", width0 + margin0.left + margin0.right)
390        .attr("height", height0 + margin0.top + margin0.bottom)
391        .append("g")
392        .attr("transform",
393            "translate(" + margin0.left + "," + margin0.top + ")");
394
395    //Read the data for graph
396    d3.csv(mean_senti_path,
397        // When reading the csv, I must format variables:
398        function (d) {
399            return {
400                date: d3.timeParse("%Y-%m-%d")(d.Date),
401                value: d.mean_senti
402            }
403        },
404
405        function (data) {
406
407            // Add X axis --> it is a date format
408            var x = d3.scaleTime()
409                .domain(d3.extent(data, function (d) {
410                    return d.date;
411                }))
412                .range([0, width0]);
413            xAxis = svg0.append("g")
414                .style("stroke", "black")
415                .style("font-size", "16px")
416                .style("font-family", "'M PLUS Rounded 1c', sans-serif")
417                .attr("transform", "translate(0," + height0 + ")")
418                .call(d3.axisBottom(x));
419
420            // Add Y axis
421            var y = d3.scaleLinear()
422                .domain([-5, 5])
423                .range([height0, 0]);
424            yAxis = svg0.append("g")
425                .style("stroke", "black")
426                .style("font-family", "'M PLUS Rounded 1c', sans-serif")
427                .style("font-size", "16px")
428                .call(d3.axisLeft(y));
429
430            // Add a clipPath: everything out of this area won't be drawn.
431            var clip = svg0.append("defs").append("svg:clipPath")
432                .attr("id", "clip")
433                .append("svg:rect")
434                .attr("width", width0)
435                .attr("height", height0)
436                .attr("x", 0)
437                .attr("y", 0);
438
439            // Add brushing
440            var brush = d3.brushX() // Add the brush feature using the d3.brush function
441                .extent([
442                    [0, 0],
443                    [width0, height0]
444                ]) // initialise the brush area: start at 0,0 and finishes at width,height: it means I select the whole graph area
445                .on("end",
446                    updateChart) // Each time the brush selection changes, trigger the 'updateChart' function
447
448            // Create the line variable: where both the line and the brush take place
449            var line = svg0.append('g')
450                .attr("clip-path", "url(#clip)")
451
452            // Add the line
453            line.append("path")
454                .datum(data)
455                .attr("class", "line") // I add the class line to be able to modify this line later on.
456                .attr("fill", "none")
457                .attr("stroke", "black")
458                .attr("stroke-width", 1.5)
459                .attr("d", d3.line()
460                    .x(function (d) {
461                        return x(d.date)
462                    })
463                    .y(function (d) {
464                        return y(d.value)
465                    })
466                )
467
468            // Add the brushing
469            line
470                .append("g")
471                .attr("class", "brush")
472                .call(brush);
473
474            // A function that set idleTimeOut to null
475            var idleTimeout
476
477            function idled() {
478                idleTimeout = null;
479            }
480
481            // A function that update the chart for given boundaries
482            function updateChart() {
483
484                // What are the selected boundaries?
485                extent = d3.event.selection
486
487                // If no selection, back to initial coordinate. Otherwise, update X axis domain
488                if (!extent) {
489                    if (!idleTimeout) return idleTimeout = setTimeout(idled,
490                        350); // This allows to wait a little bit
491                    x.domain([4, 8])
492                } else {
493                    x.domain([x.invert(extent[0]), x.invert(extent[1])])
494                    line.select(".brush").call(brush.move,
495                        null) // This remove the grey brush area as soon as the selection has been done
496                }
497
498                // Update axis and line position
499                xAxis.transition().duration(1000).call(d3.axisBottom(x))
500                line
501                    .select('.line')
502                    .transition()
503                    .duration(1000)
504                    .attr("d", d3.line()
505                        .x(function (d) {
506                            return x(d.date)
507                        })
508                        .y(function (d) {
509                            return y(d.value)
510                        })
511                    )
512            }
513
514            // If user double click, reinitialize the chart
515            svg0.on("dblclick", function () {
516                x.domain(d3.extent(data, function (d) {
517                    return d.date;
518                }))
519                xAxis.transition().call(d3.axisBottom(x))
520                line
521                    .select('.line')
522                    .transition()
523                    .attr("d", d3.line()
524                        .x(function (d) {
525                            return x(d.date)
526                        })
527                        .y(function (d) {
528                            return y(d.value)
529                        })
530                    )
531            });
532
533            /**
534             * 
535             * Reasons for not achieving good
536             * 
537             */
538             var units = "Counts";
539
540             // set the dimensions and margins of the graph
541             var margin = {top: 10, right: 10, bottom: 10, left: 10},
542                 width = 700 - margin.left - margin.right,
543                 height = 300 - margin.top - margin.bottom;
544             
545             // format variables
546             var formatNumber = d3.format(",.0f"),    // zero decimal places
547                 format = function(d) { return formatNumber(d) + " " + units; },
548                 color = d3.scaleOrdinal(d3.schemeCategory10);
549             
550             // append the svg object to the body of the page
551             var svg = d3.select("#chart").append("svg")
552                 .attr("width", width + margin.left + margin.right)
553                 .attr("height", height + margin.top + margin.bottom)
554               .append("g")
555                 .attr("transform", 
556                       "translate(" + margin.left + "," + margin.top + ")");
557             
558             // Set the sankey diagram properties
559             var sankey = d3.sankey()
560                 .nodeWidth(36)
561                 .nodePadding(40)
562                 .size([width, height]);
563             
564             var path = sankey.link();
565             
566             // load the data
567             d3.csv(rnag_path, function(error, data) {
568              
569               //set up graph in same style as original example but empty
570               graph = {"nodes" : [], "links" : []};
571             
572               data.forEach(function (d) {
573                 graph.nodes.push({ "name": d.source });
574                 graph.nodes.push({ "name": d.target });
575                 graph.links.push({ "source": d.source,
576                                    "target": d.target,
577                                    "value": +d.value });
578                });
579             
580               // return only the distinct / unique nodes
581               graph.nodes = d3.keys(d3.nest()
582                 .key(function (d) { return d.name; })
583                 .object(graph.nodes));
584             
585               // loop through each link replacing the text with its index from node
586               graph.links.forEach(function (d, i) {
587                 graph.links[i].source = graph.nodes.indexOf(graph.links[i].source);
588                 graph.links[i].target = graph.nodes.indexOf(graph.links[i].target);
589               });
590             
591               // now loop through each nodes to make nodes an array of objects
592               // rather than an array of strings
593               graph.nodes.forEach(function (d, i) {
594                 graph.nodes[i] = { "name": d };
595               });
596             
597               sankey
598                   .nodes(graph.nodes)
599                   .links(graph.links)
600                   .layout(32);
601             
602               // add in the links
603               var link = svg.append("g").selectAll(".link")
604                   .data(graph.links)
605                 .enter().append("path")
606                   .attr("class", "link")
607                   .attr("d", path)
608                   .style("stroke-width", function(d) { return Math.max(1, d.dy); })
609                   .sort(function(a, b) { return b.dy - a.dy; });
610             
611               // add the link titles
612               link.append("title")
613                     .text(function(d) {
614                         return d.source.name + " → " + 
615                             d.target.name + "\n" + format(d.value); });
616             
617               // add in the nodes
618               var node = svg.append("g").selectAll(".node")
619                   .data(graph.nodes)
620                 .enter().append("g")
621                   .attr("class", "node")
622                   .attr("transform", function(d) { 
623                       return "translate(" + d.x + "," + d.y + ")"; })
624                   .call(d3.drag()
625                     .subject(function(d) {
626                       return d;
627                     })
628                     .on("start", function() {
629                       this.parentNode.appendChild(this);
630                     })
631                     .on("drag", dragmove));
632             
633               // add the rectangles for the nodes
634               node.append("rect")
635                   .attr("height", function(d) { return d.dy; })
636                   .attr("width", sankey.nodeWidth())
637                   .style("fill", function(d) { 
638                       return d.color = color(d.name.replace(/ .*/, "")); })
639                   .style("stroke", function(d) { 
640                       return d3.rgb(d.color).darker(2); })
641                 .append("title")
642                   .text(function(d) { 
643                       return d.name + "\n" + format(d.value); });
644             
645               // add in the title for the nodes
646               node.append("text")
647                   .attr("x", -6)
648                   .attr("y", function(d) { return d.dy / 2; })
649                   .attr("dy", ".35em")
650                   .attr("text-anchor", "end")
651                   .attr("transform", null)
652                   .text(function(d) { return d.name; })
653                 .filter(function(d) { return d.x < width / 2; })
654                   .attr("x", 6 + sankey.nodeWidth())
655                   .attr("text-anchor", "start");
656             
657               // the function for moving the nodes
658               function dragmove(d) {
659                 d3.select(this)
660                   .attr("transform", 
661                         "translate(" 
662                            + d.x + "," 
663                            + (d.y = Math.max(
664                               0, Math.min(height - d.dy, d3.event.y))
665                              ) + ")");
666                 sankey.relayout();
667                 link.attr("d", path);
668               }
669             });
670
671            /**
672             * 
673             * Emmotional Classifcation Frequency
674             * 
675             */
676            // set the dimensions and margins of the graph
677            var margin1 = {
678                    top: 20,
679                    right: 20,
680                    bottom: 40,
681                    left: 185
682                },
683                width1 = 500 - margin.left - margin.right,
684                height2 = 400 - margin.top - margin.bottom;
685
686            // append the svg object to the body of the page
687            var svg1 = d3.select("#my_dataviz1")
688                .append("svg")
689                .attr("width", width1 + margin1.left + margin1.right)
690                .attr("height", height2 + margin1.top + margin1.bottom)
691                .append("g")
692                .attr("transform",
693                    "translate(" + margin1.left + "," + margin1.top + ")");
694
695            // Parse the Data
696            d3.csv(emo_freq_path, function (data) {
697
698                // Add X axis
699                var x = d3.scaleLinear()
700                    .domain([0, 50])
701                    .range([0, width]);
702                svg1.append("g")
703                    .attr("transform", "translate(0," + height + ")")
704                    .style("stroke", "black")
705                    .style("font-family", "'M PLUS Rounded 1c', sans-serif")
706                    .style("font-size", "16px")
707                    .call(d3.axisBottom(x))
708                    .selectAll("text")
709                    .attr("transform", "translate(-10,0)rotate(-45)")
710                    .style("text-anchor", "end");
711
712                // Y axis
713                var y = d3.scaleBand()
714                    .range([0, height])
715                    .domain(data.map(function (d) {
716                        return d.sentiment;
717                    }))
718                    .padding(.1);
719                svg1.append("g")
720                    .style("stroke", "black")
721                    .style("font-size", "16px")
722                    .style("font-family", "'M PLUS Rounded 1c', sans-serif")
723                    .call(d3.axisLeft(y))
724
725                //Bars
726                svg1.selectAll("myRect")
727                    .data(data)
728                    .enter()
729                    .append("rect")
730                    .attr("x", x(0))
731                    .attr("y", function (d) {
732                        return y(d.sentiment);
733                    })
734                    .attr("width", function (d) {
735                        return x(d.percent);
736                    })
737                    .attr("height", y.bandwidth())
738                    .attr("fill", "black")
739
740            })
741
742            /**
743             * 
744             * Common nounphrases word cloud
745             * 
746             */
747
748            // set the dimensions and margins of the graph
749            var margin5 = {
750                    top: 20,
751                    right: 45,
752                    bottom: 20,
753                    left: 20
754                },
755                width3 = 500 - margin.left - margin.right,
756                height3 = 400 - margin.top - margin.bottom;
757
758            // append the svg object to the body of the page
759            var svg4 = d3.select("#my_dataviz4").append("svg")
760                .attr("width", width3 + margin5.left + margin5.right)
761                .attr("height", height3 + margin5.top + margin5.bottom)
762                .append("g");
763
764            d3.csv(nounphrase_path, function (data) {
765
766
767                // Constructs a new cloud layout instance. It run an algorithm to find the position of words that suits your requirements
768                // Wordcloud features that are different from one word to the other must be here
769                var layout = d3.layout.cloud()
770                .size([width3, height3])
771                .words(data.map(function(d) { return {text: d.text, size:d.n}; }))
772                .padding(5)        //space between words
773                .rotate(function() { return ~~(Math.random() * 2) * 90; })
774                .fontSize(function(d) { return d.size*1.2; })      // font size of words
775                .on("end", draw);
776              layout.start();
777              
778              // This function takes the output of 'layout' above and draw the words
779              // Wordcloud features that are THE SAME from one word to the other can be here
780              function draw(words) {
781                svg4
782                  .append("g")
783                    .attr("transform", "translate(" + layout.size()[0] / 2 + "," + layout.size()[1] / 2 + ")")
784                    .selectAll("text")
785                      .data(words)
786                    .enter().append("text")
787                      .style("fill", "black")
788                      .attr("text-anchor", "middle")
789                      .style("font-family", "Impact")
790                      .style("font-size", function(d) {return d.size + "px"; })
791                      .attr("transform", function(d) {
792                        return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
793                      })
794                      .text(function(d) { return d.text; });
795              }
796
797            });
798
799        })
800}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.