--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('raw', [
4 | 'ngRoute',
5 | 'ngAnimate',
6 | 'ngSanitize',
7 | 'raw.filters',
8 | 'raw.services',
9 | 'raw.directives',
10 | 'raw.controllers',
11 | 'mgcrea.ngStrap',
12 | 'ui',
13 | 'colorpicker.module'
14 | ])
15 |
16 | .config(['$routeProvider','$locationProvider', function ($routeProvider,$locationProvider) {
17 | $routeProvider.when('/', {templateUrl: 'partials/main.html', controller: 'RawCtrl'});
18 | $routeProvider.otherwise({redirectTo: '/'});
19 | $locationProvider.html5Mode(true);
20 | }]);
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c), 2013-2014 DensityDesign Lab, Giorgio Caviglia, Michele Mauri, Giorgio Uboldi, Matteo Azzi
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | This program is free software: you can redistribute it and/or modify
10 | it under the terms of the GNU Lesser General Public License as published by
11 | the Free Software Foundation, either version 3 of the License, or
12 | (at your option) any later version.
13 |
14 | This program is distributed in the hope that it will be useful,
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | GNU Lesser General Public License for more details.
18 |
19 | You should have received a copy of the GNU Lesser General Public License
20 | along with this program. If not, see .
21 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Raw",
3 | "version": "1.0.0",
4 | "authors": [
5 | "DensityDesign Lab"
6 | ],
7 | "description": "The missing link between spreadsheet and vector graphics",
8 | "main": "index.html",
9 | "keywords": [
10 | "Raw"
11 | ],
12 | "license": "LGPL v.3",
13 | "homepage": "raw.densitydesign.org",
14 | "ignore": [
15 | "**/.*",
16 | "node_modules",
17 | "bower_components",
18 | "test",
19 | "tests"
20 | ],
21 | "dependencies": {
22 | "angular": "1.2.14",
23 | "d3": "~3.4.3",
24 | "bootstrap": "3.1.1",
25 | "angular-route": "1.2.14",
26 | "angular-strap": "2.0.0-rc.4",
27 | "angular-animate": "1.2.14",
28 | "jquery-ui": "1.10.4",
29 | "angular-sanitize": "1.2.14",
30 | "angular-bootstrap-colorpicker": "1.0",
31 | "bootstrap-colorpicker": "*",
32 | "d3-plugins": "*",
33 | "FileSaver": "*",
34 | "angular-ui": "0.4.0",
35 | "zeroclipboard": "1.3.2",
36 | "canvas-toBlob.js": "https://github.com/eligrey/canvas-toBlob.js.git",
37 | "jqueryui-touch-punch": "*",
38 | "codemirror": "~4.5.0"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/templates/colors.html:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
{{color.key}}
19 |
20 |
21 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/js/services.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* Services */
4 |
5 | angular.module('raw.services', [])
6 |
7 | .factory('dataService', function ($http, $q, $timeout) {
8 |
9 | return {
10 |
11 | loadSample : function(sample){
12 | var deferred = $q.defer();
13 | $http.get(sample)
14 | .then(function(response){
15 | deferred.resolve(response.data);
16 | },
17 | function(){
18 | deferred.reject("An error occured while getting sample (" + sample.title + ")");
19 | });
20 |
21 | return deferred.promise;
22 | },
23 |
24 | debounce : function (func, wait, immediate) {
25 | var timeout;
26 | var deferred = $q.defer();
27 | return function() {
28 | var context = this, args = arguments;
29 | var later = function() {
30 | timeout = null;
31 | if(!immediate) {
32 | deferred.resolve(func.apply(context, args));
33 | deferred = $q.defer();
34 | }
35 | };
36 | var callNow = immediate && !timeout;
37 | if ( timeout ) {
38 | $timeout.cancel(timeout);
39 | }
40 | timeout = $timeout(later, wait);
41 | if (callNow) {
42 | deferred.resolve(func.apply(context,args));
43 | deferred = $q.defer();
44 | }
45 | return deferred.promise;
46 | };
47 | }
48 |
49 | }
50 | })
51 |
--------------------------------------------------------------------------------
/data/dispersions.csv:
--------------------------------------------------------------------------------
1 | Movie,Genre,Production Budget,Total Domestic Box Office,Rating IMDB
2 | Avatar,Action,425000000,760507625, 8.0
3 | The Blind Side,Drama,35000000,255959475, 7.6
4 | "The Chronicles of Narnia: The Lion, the Witch and the Wardrobe",Adventure,180000000,291710957, 6.9
5 | The Dark Knight,Action,185000000,533345358, 9.0
6 | ET: The Extra-Terrestrial,Drama,10500000,435110554, 7.9
7 | Finding Nemo,Adventure,94000000,380529370, 8.1
8 | Ghostbusters,Comedy,30000000,238632124, 7.8
9 | The Hunger Games,Thriller/Suspense,80000000,408010692, 7.2
10 | Iron Man 3,Action,200000000,396702239, 7.6
11 | Jurassic Park,Action,63000000,395708305, 8.0
12 | King Kong,Adventure,207000000,218080025, 7.3
13 | The Lion King,Adventure,79300000,422780140, 8.4
14 | "Monsters, Inc.",Adventure,115000000,289423425, 8.0
15 | The Twilight Saga: New Moon,Drama,50000000,296623634, 4.5
16 | Oz the Great and Powerful,Adventure,200000000,233671832, 6.6
17 | Pirates of the Caribbean: Dead Man's Chest,Adventure,225000000,423315812, 7.3
18 | Quantum of Solace,Action,230000000,169368427, 6.7
19 | Raiders of the Lost Ark,Adventure,20000000,248159971, 8.7
20 | Star Wars Ep. I: The Phantom Menace,Adventure,115000000,474544677, 6.5
21 | Titanic,Thriller/Suspense,200000000,658672302, 7.6
22 | Up,Adventure,175000000,293004164, 8.3
23 | The Vow,Drama,30000000,125014030, 6.7
24 | The War of the Worlds,Action,132000000,234280354, 6.5
25 | X-Men: The Last Stand,Action,210000000,234362462, 6.8
26 | You've Got Mail,Drama,65000000,115821495, 6.3
27 | Zookeeper,Romantic Comedy,80000000,80360866, 5.0
--------------------------------------------------------------------------------
/charts/delaunay.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var points = raw.models.points();
4 |
5 | points.dimensions().remove('size');
6 | points.dimensions().remove('label');
7 | points.dimensions().remove('color');
8 |
9 | var chart = raw.chart()
10 | .title('Delaunay Triangulation')
11 | .description(
12 | "The Delaunay triangulation, the dual of Voronoi tesselation, creates a planar, triangular mesh for a given set of points. Based on http://bl.ocks.org/mbostock/4341156")
13 | .thumbnail("imgs/delaunay.png")
14 | .model(points)
15 |
16 | var width = chart.number()
17 | .title("Width")
18 | .defaultValue(1000)
19 | .fitToWidth(true)
20 |
21 | var height = chart.number()
22 | .title("Height")
23 | .defaultValue(500)
24 |
25 | chart.draw(function (selection, data){
26 |
27 | var x = d3.scale.linear().range([0,+width()]).domain(d3.extent(data, function (d){ return d.x; })),
28 | y = d3.scale.linear().range([+height(), 0]).domain(d3.extent(data, function (d){ return d.y; }));
29 |
30 | var delaunay = d3.geom.voronoi()
31 | .x(function (d){ return x(d.x); })
32 | .y(function (d){ return y(d.y); })
33 | .clipExtent([ [ 0, 0 ], [+width(), +height()] ]);
34 |
35 | var g = selection
36 | .attr("width", +width())
37 | .attr("height", +height())
38 | .append("g");
39 |
40 | var path = g.selectAll("path")
41 | .data(delaunay.triangles(data), polygon)
42 | .enter().append("path")
43 | .style("fill", "#bbb")
44 | .style("stroke","#fff")
45 | .attr("d", polygon);
46 |
47 | function polygon(d) {
48 | if(!d) return;
49 | var s = d.map(function (a){ return [x(a.x), y(a.y)] } )
50 | return "M" + s.join("L") + "Z";
51 | }
52 |
53 | })
54 | })();
55 |
--------------------------------------------------------------------------------
/data/correlations.csv:
--------------------------------------------------------------------------------
1 | Cocktail,Parts,Ingredient
2 | Bloody Mary,9,vodka
3 | Bloody Mary,18,Tomato juice
4 | Bloody Mary,3,lemon juice
5 | Gin and Tonic,12,Gin
6 | Gin and Tonic,29,Tonic Water
7 | Screwdriver,10,vodka
8 | Screwdriver,20,orange juice
9 | White Russian,10,vodka
10 | White Russian,4,coffee liqueur
11 | White Russian,6,cream
12 | Cosmopolitan,8,vodka
13 | Cosmopolitan,3,cointreau
14 | Cosmopolitan,3,lime juice
15 | Cosmopolitan,6,cranberry juice
16 | Apple Martini,8,vodka
17 | Apple Martini,3,apple schnapps
18 | Apple Martini,3,cointreau
19 | Long Island Iced Tea,3,vodka
20 | Long Island Iced Tea,3,tequila
21 | Long Island Iced Tea,3,rum
22 | Long Island Iced Tea,3,triple sec
23 | Long Island Iced Tea,3,Gin
24 | Long Island Iced Tea,5,lemon juice
25 | Long Island Iced Tea,6,gomme syrup
26 | Mudslide,6,vodka
27 | Mudslide,6,coffee liqueur
28 | Mudslide,6,bailey's
29 | Mudslide,6,cream
30 | Margarita,7,tequila
31 | Margarita,4,cointreau
32 | Margarita,3,lime juice
33 | Kamikaze,6,vodka
34 | Kamikaze,6,triple sec
35 | Kamikaze,6,lime juice
36 | Mojito,8,rum
37 | Mojito,6,lime juice
38 | Mojito,1,sugar
39 | Mojito,1,mint
40 | Mojito,12,soda
41 | Caribou Lou,6,rum
42 | Caribou Lou,3,pineapple juice
43 | Cuba Libre,12,Cola
44 | Cuba Libre,6,rum
45 | Jager Monster,9,Jagermeister
46 | Jager Monster,6,orange juice
47 | Daiquiri,9,rum
48 | Daiquiri,4,lime juice
49 | Daiquiri,1,syrup
50 | Whiskey Sour,3,whiskey
51 | Whiskey Sour,2,lemon juice
52 | Whiskey Sour,1,syrup
53 | Mint Julep,18,whiskey
54 | Mint Julep,1,mint
55 | Mint Julep,1,sugar
56 | Pina Colada,6,rum
57 | Pina Colada,6,cream
58 | Pina Colada,6,pineapple juice
59 | Sex on the Beach,8,vodka
60 | Sex on the Beach,4,peach schnapps
61 | Sex on the Beach,8,orange juice
62 | Sex on the Beach,8,cranberry juice
63 | B-52,4,coffee liqueur
64 | B-52,4,bailey's
65 | B-52,4,cointreau
--------------------------------------------------------------------------------
/charts/convexHull.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var points = raw.models.points();
4 |
5 | points.dimensions().remove('size');
6 | points.dimensions().remove('label');
7 | points.dimensions().remove('color');
8 |
9 | var chart = raw.chart()
10 | .title('Convex Hull')
11 | .description(
12 | "In mathematics, the convex hull is the smallest convex shape containing a set o points. Applied to a scatterplot, it is useful to identify points belonging to the same category.
Based on http://bl.ocks.org/mbostock/4341699")
13 | .thumbnail("imgs/convexHull.png")
14 | .model(points)
15 |
16 | var width = chart.number()
17 | .title("Width")
18 | .defaultValue(1000)
19 | .fitToWidth(true)
20 |
21 | var height = chart.number()
22 | .title("Height")
23 | .defaultValue(500)
24 |
25 | var stroke = chart.number()
26 | .title("Stroke Width")
27 | .defaultValue(32)
28 |
29 | chart.draw(function (selection, data){
30 |
31 | var x = d3.scale.linear().range([0,+width()-stroke()]).domain(d3.extent(data, function (d){ return d.x; })),
32 | y = d3.scale.linear().range([+height()-stroke(), 0]).domain(d3.extent(data, function (d){ return d.y; }));
33 |
34 | var vertices = data.map(function (d){
35 | return [ x(d.x), y(d.y) ]
36 | })
37 |
38 | var g = selection
39 | .attr("width", +width())
40 | .attr("height", +height())
41 | .append("g")
42 | .attr("transform","translate(" + stroke()/2 + "," + stroke()/2 + ")")
43 |
44 | g.append("path")
45 | .datum(d3.geom.hull(vertices))
46 | .style("fill", "#bbb")
47 | .style("stroke","#bbb")
48 | .style("stroke-width", +stroke())
49 | .style("stroke-linejoin","round")
50 | .attr("d", function (d) { return "M" + d.join("L") + "Z"; });
51 |
52 | g.selectAll("circle")
53 | .data(vertices)
54 | .enter().append("circle")
55 | .attr("r", 2)
56 | .attr("transform", function (d) { return "translate(" + d + ")"; })
57 | })
58 | })();
--------------------------------------------------------------------------------
/charts/voronoi.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var points = raw.models.points();
4 |
5 | points.dimensions().remove('size');
6 | points.dimensions().remove('label');
7 |
8 | var chart = raw.chart()
9 | .title('Voronoi Tessellation')
10 | .description(
11 | "It creates the minimum area around each point defined by two variables. When applied to a scatterplot, it is useful to show the distance between points. Based on http://bl.ocks.org/mbostock/4060366")
12 | .thumbnail("imgs/voronoi.png")
13 | .category('Distributions')
14 | .model(points)
15 |
16 | var width = chart.number()
17 | .title("Width")
18 | .defaultValue(1000)
19 | .fitToWidth(true)
20 |
21 | var height = chart.number()
22 | .title("Height")
23 | .defaultValue(500)
24 |
25 | var colors = chart.color()
26 | .title("Color scale")
27 |
28 | var showPoints = chart.checkbox()
29 | .title("Show points")
30 | .defaultValue(true)
31 |
32 | chart.draw(function (selection, data){
33 |
34 | var x = d3.scale.linear().range([0,+width()]).domain(d3.extent(data, function (d){ return d.x; })),
35 | y = d3.scale.linear().range([+height(), 0]).domain(d3.extent(data, function (d){ return d.y; }));
36 |
37 | var voronoi = d3.geom.voronoi()
38 | .x(function (d){ return x(d.x); })
39 | .y(function (d){ return y(d.y); })
40 | .clipExtent([ [ 0, 0 ], [+width(), +height()] ]);
41 |
42 | var g = selection
43 | .attr("width", +width())
44 | .attr("height", +height())
45 | .append("g");
46 |
47 | colors.domain(data, function (d){ return d.color; });
48 |
49 | var path = g.selectAll("path")
50 | .data(voronoi(data), polygon)
51 | .enter().append("path")
52 | .style("fill",function (d){ return d && colors()? colors()(d.point.color) : "#dddddd"; })
53 | .style("stroke","#fff")
54 | .attr("d", polygon);
55 |
56 | path.order();
57 |
58 | g.selectAll("circle")
59 | .data(data.filter(function(){ return showPoints() }))
60 | .enter().append("circle")
61 | .style("fill","#000000")
62 | .style("pointer-events","none")
63 | .attr("transform", function (d) { return "translate(" + x(d.x) + ", " + y(d.y) + ")"; })
64 | .attr("r", 1.5);
65 |
66 | function polygon(d) {
67 | if(!d) return;
68 | return "M" + d.join("L") + "Z";
69 | }
70 |
71 | })
72 | })();
73 |
--------------------------------------------------------------------------------
/charts/chart.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | // A simple scatterplot for APIs demo
4 |
5 | // The Model
6 |
7 | var model = raw.model();
8 |
9 | // X axis dimension
10 | // Adding a title to be displayed in the UI
11 | // and limiting the type of data to Numbers only
12 | var x = model.dimension()
13 | .title('X Axis')
14 | .types(Number)
15 |
16 | // Y axis dimension
17 | // Same as X
18 | var y = model.dimension()
19 | .title('Y Axis')
20 | .types(Number)
21 |
22 | // Mapping function
23 | // For each record in the data returns the values
24 | // for the X and Y dimensions and casts them as numbers
25 | model.map(function (data){
26 | return data.map(function (d){
27 | return {
28 | x : +x(d),
29 | y : +y(d)
30 | }
31 | })
32 | })
33 |
34 |
35 | // The Chart
36 |
37 | var chart = raw.chart()
38 | .title("Simple Scatter Plot")
39 | .description("A simple chart for test")
40 | .model(model)
41 |
42 | // Some options we want to expose to the users
43 | // For each of them a GUI component will be created
44 | // Options can be use within the Draw function
45 | // by simply calling them (i.e. witdh())
46 | // the current value of the options will be returned
47 |
48 | // Width
49 | var width = chart.number()
50 | .title('Width')
51 | .defaultValue(900)
52 |
53 | // Height
54 | var height = chart.number()
55 | .title('Height')
56 | .defaultValue(600)
57 |
58 | // A simple margin
59 | var margin = chart.number()
60 | .title('margin')
61 | .defaultValue(10)
62 |
63 | // Drawing function
64 | // selection represents the d3 selection (svg)
65 | // data is not the original set of records
66 | // but the result of the model map function
67 | chart.draw(function (selection, data){
68 |
69 | // svg size
70 | selection
71 | .attr("width", width())
72 | .attr("height", height())
73 |
74 | // x and y scale
75 | var xScale = d3.scale.linear().domain([0, d3.max(data, function (d){ return d.x; })]).range([margin(), width()-margin()]);
76 | var yScale = d3.scale.linear().domain([0, d3.max(data, function (d){ return d.y; })]).range([height()-margin(), margin()]);
77 |
78 | // let's plot the dots!
79 | selection.selectAll("circle")
80 | .data(data)
81 | .enter().append("circle")
82 | .attr("cx", function(d) { return xScale(d.x); })
83 | .attr("cy", function(d) { return yScale(d.y); })
84 | .attr("r", 5)
85 |
86 | })
87 | })();
--------------------------------------------------------------------------------
/charts/circularDendrogram.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var tree = raw.models.tree();
4 |
5 | tree.dimensions().remove('size');
6 | tree.dimensions().remove('color');
7 | tree.dimensions().remove('label');
8 |
9 | var chart = raw.chart()
10 | .title('Circular Dendrogram')
11 | .description(
12 | "Dendrograms are tree-like diagrams used to represent the distribution of a hierarchical clustering. The different depth levels represented by each node are visualized on the horizontal axes and it is useful to visualize a non-weighted hierarchy. Based on http://bl.ocks.org/mbostock/4063570")
13 | .thumbnail("imgs/circularDendrogram.png")
14 | .category('Hierarchies')
15 | .model(tree)
16 |
17 | var diameter = chart.number ()
18 | .title("Radius")
19 | .defaultValue(1000)
20 | .fitToWidth(true)
21 |
22 | chart.draw(function (selection, data){
23 |
24 | var g = selection
25 | .attr("width", +diameter() )
26 | .attr("height", +diameter() )
27 | .append("g")
28 | .attr("transform", "translate(" + diameter()/2 + "," + diameter()/2 + ")");
29 |
30 | var cluster = d3.layout.cluster()
31 | .size([360, diameter()/2-120]);
32 |
33 | var diagonal = d3.svg.diagonal.radial()
34 | .projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });
35 |
36 | var nodes = cluster.nodes(data);
37 |
38 | var link = g.selectAll("path.link")
39 | .data(cluster.links(nodes))
40 | .enter().append("path")
41 | .attr("class", "link")
42 | .style("fill","none")
43 | .style("stroke","#cccccc")
44 | .style("stroke-width","1px")
45 | .attr("d", diagonal);
46 |
47 | var node = g.selectAll("g.node")
48 | .data(nodes)
49 | .enter().append("g")
50 | .attr("class", "node")
51 | .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
52 |
53 | node.append("circle")
54 | .attr("r", 4.5)
55 | .style("fill", "#eeeeee")
56 | .style("stroke","#999999")
57 | .style("stroke-width","1px");
58 |
59 | node.append("text")
60 | .attr("dy", ".31em")
61 | .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
62 | .attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; })
63 | .text(function(d) { return d.name; })
64 | .style("font-size","11px")
65 | .style("font-family","Arial, Helvetica")
66 |
67 | })
68 | })();
69 |
--------------------------------------------------------------------------------
/charts/reingoldTilford.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var tree = raw.models.tree();
4 |
5 | tree.dimensions().remove('size');
6 | tree.dimensions().remove('color');
7 | tree.dimensions().remove('label');
8 |
9 | var chart = raw.chart()
10 | .title('Reingold–Tilford Tree')
11 | .description(
12 | "The tree layout implements the Reingold-Tilford algorithm for efficient, tidy arrangement of layered nodes. The depth of nodes is computed by distance from the root, leading to a ragged appearance. Based on http://bl.ocks.org/mbostock/4339184")
13 | .thumbnail("imgs/reingoldTilford.png")
14 | .category('Hierarchies')
15 | .model(tree)
16 |
17 | var width = chart.number()
18 | .title("Width")
19 | .defaultValue(1000)
20 | .fitToWidth(true)
21 |
22 | var height = chart.number()
23 | .title("Height")
24 | .defaultValue(500)
25 |
26 | chart.draw(function (selection, data){
27 |
28 | var g = selection
29 | .attr("width", +width() )
30 | .attr("height", +height() )
31 | .append("g")
32 | .attr("transform", "translate(40,0)");
33 |
34 | var layout = d3.layout.tree()
35 | .size([+height(), +width() -160]);
36 |
37 | var diagonal = d3.svg.diagonal()
38 | .projection(function (d) { return [d.y, d.x]; });
39 |
40 | var nodes = layout.nodes(data),
41 | links = layout.links(nodes);
42 |
43 | var link = g.selectAll("path.link")
44 | .data(links)
45 | .enter().append("path")
46 | .attr("class", "link")
47 | .style("fill","none")
48 | .style("stroke","#cccccc")
49 | .style("stroke-width","1px")
50 | .attr("d", diagonal);
51 |
52 | var node = g.selectAll("g.node")
53 | .data(nodes)
54 | .enter().append("g")
55 | .attr("class", "node")
56 | .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
57 |
58 | node.append("circle")
59 | .style("fill", "#eeeeee")
60 | .style("stroke","#999999")
61 | .style("stroke-width","1px")
62 | .attr("r", 4.5);
63 |
64 | node.append("text")
65 | .attr("dx", function(d) { return d.children ? -8 : 8; })
66 | .attr("dy", 3)
67 | .style("font-size","11px")
68 | .style("font-family","Arial, Helvetica")
69 | .attr("text-anchor", function(d) { return d.children ? "end" : "start"; })
70 | .text(function (d){ return d.name; });
71 |
72 | })
73 | })();
74 |
--------------------------------------------------------------------------------
/charts/clusterDendrogram.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var tree = raw.models.tree();
4 |
5 | tree.dimensions().remove('size');
6 | tree.dimensions().remove('color');
7 | tree.dimensions().remove('label');
8 |
9 | var chart = raw.chart()
10 | .title('Cluster Dendrogram')
11 | .description(
12 | "Dendrograms are tree-like diagrams used to represent the distribution of a hierarchical clustering. The different depth levels represented by each node are visualized on the horizontal axes and it is useful to visualize a non-weighted hierarchy. Based on http://bl.ocks.org/mbostock/4063570")
13 | .thumbnail("imgs/dendrogram.png")
14 | .category('Hierarchies')
15 | .model(tree)
16 |
17 | var width = chart.number()
18 | .title("Width")
19 | .defaultValue(1000)
20 | .fitToWidth(true)
21 |
22 | var height = chart.number()
23 | .title("Height")
24 | .defaultValue(500)
25 |
26 | chart.draw(function (selection, data){
27 |
28 | var g = selection
29 | .attr("width", +width() )
30 | .attr("height", +height() )
31 | .append("g")
32 | .attr("transform", "translate(40,0)");
33 |
34 | var cluster = d3.layout.cluster()
35 | .size([+height(), +width() - 160]);
36 |
37 | var diagonal = d3.svg.diagonal()
38 | .projection(function (d) { return [d.y, d.x]; });
39 |
40 | var nodes = cluster.nodes(data),
41 | links = cluster.links(nodes);
42 |
43 | var link = g.selectAll(".link")
44 | .data(links)
45 | .enter().append("path")
46 | .attr("class", "link")
47 | .style("fill","none")
48 | .style("stroke","#cccccc")
49 | .style("stroke-width","1px")
50 | .attr("d", diagonal);
51 |
52 | var node = g.selectAll(".node")
53 | .data(nodes)
54 | .enter().append("g")
55 | .attr("class", "node")
56 | .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
57 |
58 | node.append("circle")
59 | .attr("r", 4.5)
60 | .style("fill", "#eeeeee")
61 | .style("stroke","#999999")
62 | .style("stroke-width","1px")
63 |
64 | node.append("text")
65 | .style("font-size","11px")
66 | .style("font-family","Arial, Helvetica")
67 | .attr("dx", function(d) { return d.children ? -8 : 8; })
68 | .attr("dy", 3)
69 | .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
70 | .text(function(d) { return d.name; });
71 |
72 | })
73 | })();
--------------------------------------------------------------------------------
/charts/treemap.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var tree = raw.models.tree();
4 |
5 | var chart = raw.chart()
6 | .title('Treemap')
7 | .description(
8 | "A space filling visualization of data hierarchies and proportion between elements. The different hierarchical levels create visual clusters through the subdivision into rectangles proportionally to each element's value. Treemaps are useful to represent the different proportion of nested hierarchical data structures. Based on http://bl.ocks.org/mbostock/4063582")
9 | .thumbnail("imgs/treemap.png")
10 | .category('Hierarchies')
11 | .model(tree)
12 |
13 | var width = chart.number()
14 | .title('Width')
15 | .defaultValue(100)
16 | .fitToWidth(true)
17 |
18 | var height = chart.number()
19 | .title("Height")
20 | .defaultValue(500)
21 |
22 | var padding = chart.number()
23 | .title("Padding")
24 | .defaultValue(5)
25 |
26 | var colors = chart.color()
27 | .title("Color scale")
28 |
29 | chart.draw(function (selection, data){
30 |
31 | var format = d3.format(",d");
32 |
33 | var layout = d3.layout.treemap()
34 | .sticky(true)
35 | .padding(+padding())
36 | .size([+width(), +height()])
37 | .value(function(d) { return d.size; })
38 |
39 | var g = selection
40 | .attr("width", +width())
41 | .attr("height", +height())
42 | .append("g")
43 | .attr("transform", "translate(.5,.5)");
44 |
45 | var nodes = layout.nodes(data)
46 | .filter(function(d) { return !d.children; });
47 |
48 | colors.domain(nodes, function (d){ return d.color; });
49 |
50 | var cell = g.selectAll("g")
51 | .data(nodes)
52 | .enter().append("g")
53 | .attr("class", "cell")
54 | .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
55 |
56 | cell.append("svg:rect")
57 | .attr("width", function (d) { return d.dx; })
58 | .attr("height", function (d) { return d.dy; })
59 | .style("fill", function (d) { return colors()(d.color); })
60 | .style("fill-opacity", function (d) { return d.children ? 0 : 1; })
61 | .style("stroke","#fff")
62 |
63 | cell.append("svg:title")
64 | .text(function(d) { return d.name + ": " + format(d.size); });
65 |
66 | cell.append("svg:text")
67 | .attr("x", function(d) { return d.dx / 2; })
68 | .attr("y", function(d) { return d.dy / 2; })
69 | .attr("dy", ".35em")
70 | .attr("text-anchor", "middle")
71 | // .attr("fill", function (d) { return raw.foreground(color()(d.color)); })
72 | .style("font-size","11px")
73 | .style("font-family","Arial, Helvetica")
74 | .text(function(d) { return d.label ? d.label.join(", ") : d.name; });
75 |
76 | })
77 | })();
--------------------------------------------------------------------------------
/charts/packing.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var tree = raw.models.tree();
4 |
5 | var chart = raw.chart()
6 | .title('Circle Packing')
7 | .description(
8 | "Nested circles allow to represent hierarchies and compare values. This visualization is particularly effective to show the proportion between elements through their areas and their position inside a hierarchical structure. Based on http://bl.ocks.org/mbostock/4063530")
9 | .thumbnail("imgs/circlePacking.png")
10 | .category('Hierarchies')
11 | .model(tree)
12 |
13 | var diameter = chart.number()
14 | .title("Diameter")
15 | .defaultValue(800)
16 | .fitToWidth(true)
17 |
18 | var padding = chart.number()
19 | .title("Padding")
20 | .defaultValue(5)
21 |
22 | var sort = chart.checkbox()
23 | .title("Sort by size")
24 | .defaultValue(false)
25 |
26 | var colors = chart.color()
27 | .title("Color scale")
28 |
29 | var showLabels = chart.checkbox()
30 | .title("Show labels")
31 | .defaultValue(true)
32 |
33 | chart.draw(function (selection, data){
34 |
35 | if (!data.children.length) return;
36 |
37 | var margin = 10,
38 | outerDiameter = +diameter(),
39 | innerDiameter = outerDiameter - margin - margin;
40 |
41 | var x = d3.scale.linear()
42 | .range([0, innerDiameter]);
43 |
44 | var y = d3.scale.linear()
45 | .range([0, innerDiameter]);
46 |
47 | var pack = d3.layout.pack()
48 | .padding(+padding())
49 | .sort(function (a,b){ return sort() ? a.value-b.value : null; })
50 | .size([innerDiameter, innerDiameter])
51 | .value(function(d) { return +d.size; })
52 |
53 | var g = selection
54 | .attr("width", outerDiameter)
55 | .attr("height", outerDiameter)
56 | .append("g")
57 | .attr("transform", "translate(" + margin + "," + margin + ")");
58 |
59 | var focus = data,
60 | nodes = pack.nodes(data);
61 |
62 | colors.domain(nodes.filter(function (d){ return !d.children; }), function (d){ return d.color; });
63 |
64 | g.append("g").selectAll("circle")
65 | .data(nodes)
66 | .enter().append("circle")
67 | .attr("class", function (d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
68 | .attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; })
69 | .attr("r", function (d) { return d.r; })
70 | .style("fill", function (d) { return !d.children ? colors()(d.color) : ''; })
71 | .style("fill-opacity", function (d){ return !d.children ? 1 : 0; })
72 | .style("stroke", '#ddd')
73 | .style("stroke-opacity", function (d) { return !d.children ? 0 : 1 })
74 |
75 | g.append("g").selectAll("text")
76 | .data(nodes.filter(function (d){ return showLabels(); }))
77 | .enter().append("text")
78 | .attr("text-anchor", "middle")
79 | .style("font-size","11px")
80 | .style("font-family","Arial, Helvetica")
81 | .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
82 | .text(function (d) { return d.label ? d.label.join(", ") : d.name; });
83 |
84 | })
85 | })();
--------------------------------------------------------------------------------
/charts/hexagonalBinning.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var points = raw.models.points();
4 |
5 | points.dimensions().remove('size');
6 | points.dimensions().remove('label');
7 | points.dimensions().remove('color');
8 |
9 | var chart = raw.chart()
10 | .title('Hexagonal Binning')
11 | .description(
12 | "Visually clusters the most populated areas on a scatterplot. Useful to make more readable a scatterplot when plotting hundreds of points. Based on http://bl.ocks.org/mbostock/4248145")
13 | .thumbnail("imgs/binning.png")
14 | .category('Distributions')
15 | .model(points)
16 |
17 | var width = chart.number()
18 | .title("Width")
19 | .defaultValue(1000)
20 | .fitToWidth(true)
21 |
22 | var height = chart.number()
23 | .title("Height")
24 | .defaultValue(500)
25 |
26 | var radius = chart.number()
27 | .title("Radius")
28 | .defaultValue(20)
29 |
30 | var useZero = chart.checkbox()
31 | .title("set origin at (0,0)")
32 | .defaultValue(false)
33 |
34 | var colors = chart.color()
35 | .title("Color scale")
36 |
37 | var showPoints = chart.checkbox()
38 | .title("show points")
39 | .defaultValue(true)
40 |
41 | chart.draw(function (selection, data){
42 |
43 | // Retrieving dimensions from model
44 | var x = points.dimensions().get('x'),
45 | y = points.dimensions().get('y');
46 |
47 | var g = selection
48 | .attr("width", +width() )
49 | .attr("height", +height() )
50 | .append("g")
51 |
52 | var marginLeft = d3.max(data, function (d) { return (Math.log(d.y) / 2.302585092994046) + 1; }) * 9,
53 | marginBottom = 20,
54 | w = width() - marginLeft,
55 | h = height() - marginBottom;
56 |
57 | var xExtent = !useZero()? d3.extent(data, function (d){ return d.x; }) : [0, d3.max(data, function (d){ return d.x; })],
58 | yExtent = !useZero()? d3.extent(data, function (d){ return d.y; }) : [0, d3.max(data, function (d){ return d.y; })];
59 |
60 | var xScale = x.type() == "Date"
61 | ? d3.time.scale().range([marginLeft,width()]).domain(xExtent)
62 | : d3.scale.linear().range([marginLeft,width()]).domain(xExtent),
63 | yScale = y.type() == "Date"
64 | ? d3.time.scale().range([h, 0]).domain(yExtent)
65 | : d3.scale.linear().range([h, 0]).domain(yExtent),
66 | xAxis = d3.svg.axis().scale(xScale).tickSize(-h).orient("bottom"),
67 | yAxis = d3.svg.axis().scale(yScale).ticks(10).tickSize(-w).orient("left");
68 |
69 | var hexbin = d3.hexbin()
70 | .size([w, h])
71 | .x(function(d){ return xScale(d.x); })
72 | .y(function(d){ return yScale(d.y); })
73 | .radius(+radius());
74 |
75 | var xAxis = d3.svg.axis()
76 | .scale(xScale)
77 | .orient("bottom")
78 | .tickSize(6, -h);
79 |
80 | var yAxis = d3.svg.axis()
81 | .scale(yScale)
82 | .orient("left")
83 | .tickSize(6, -w);
84 |
85 | g.append("clipPath")
86 | .attr("id", "clip")
87 | .append("rect")
88 | .attr("class", "mesh")
89 | .attr("width", w)
90 | .attr("height", h)
91 | .attr("transform", "translate(" + marginLeft + ",1)");
92 |
93 | colors.domain(hexbin(data), function (d){ return d.length; });
94 |
95 | g.append("g")
96 | .attr("clip-path", "url(#clip)")
97 | .selectAll(".hexagon")
98 | .data(hexbin(data))
99 | .enter().append("path")
100 | .attr("class", "hexagon")
101 | .attr("d", hexbin.hexagon())
102 | .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
103 | .style("fill", function(d) { return colors()(d.length); })
104 | .attr("stroke","#000")
105 | .attr("stroke-width",".5px")
106 |
107 | var point = g.selectAll("g.point")
108 | .data(data)
109 | .enter().append("g")
110 | .attr("class","point")
111 |
112 | point.append("circle")
113 | .filter(function(){ return showPoints(); })
114 | .style("fill", "#000")
115 | .attr("transform", function(d) { return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"; })
116 | .attr("r", 1);
117 |
118 | g.append("g")
119 | .attr("class", "y axis")
120 | .attr("transform", "translate(" + marginLeft + ",0)")
121 | .call(yAxis);
122 |
123 | g.append("g")
124 | .attr("class", "x axis")
125 | .attr("transform", "translate(0," + h + ")")
126 | .call(xAxis);
127 |
128 | g.selectAll(".axis")
129 | .selectAll("text")
130 | .style("font","10px Arial, Helvetica")
131 |
132 | g.selectAll(".axis")
133 | .selectAll("path")
134 | .style("fill","none")
135 | .style("stroke","#000000")
136 | .style("shape-rendering","crispEdges")
137 |
138 | g.selectAll(".axis")
139 | .selectAll("line")
140 | .style("fill","none")
141 | .style("stroke","#000000")
142 | .style("shape-rendering","crispEdges")
143 | })
144 | })();
145 |
--------------------------------------------------------------------------------
/charts/parallelCoordinates.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var model = raw.model();
4 |
5 | var list = model.dimension()
6 | .title('Dimensions')
7 | .multiple(true)
8 | .types(Number)
9 | .required(2);
10 |
11 | var color = model.dimension()
12 | .title("Color")
13 |
14 | model.map(function (data){
15 | if (!list()) return;
16 | return data.map(function (d){
17 | var obj = { dimensions: {}, color: color(d) };
18 | list().forEach(function (l){
19 | obj.dimensions[l] = d[l];
20 | })
21 | return obj;
22 | })
23 | })
24 |
25 | var chart = raw.chart()
26 | .title('Parallel Coordinates')
27 | .description(
28 | "Parallel coordinates is a common way of visualizing high-dimensional geometry and analyzing multivariate data.To show a set of points in an n-dimensional space, a backdrop is drawn consisting of n parallel lines, typically vertical and equally spaced. A point in n-dimensional space is represented as a polyline with vertices on the parallel axes; the position of the vertex on the ith axis corresponds to the ith coordinate of the point. Based on http://bl.ocks.org/jasondavies/1341281")
29 | .thumbnail("imgs/parallelCoordinates.png")
30 | .category('Distributions')
31 | .model(model)
32 |
33 | var width = chart.number()
34 | .title("Width")
35 | .defaultValue(1000)
36 | .fitToWidth(true)
37 |
38 | var height = chart.number()
39 | .title("Height")
40 | .defaultValue(500)
41 |
42 | var colors = chart.color()
43 | .title("Color scale")
44 |
45 | chart.draw(function (selection, data){
46 |
47 | var m = [30, 40, 10, 40],
48 | w = +width() - m[1] - m[3],
49 | h = +height() - m[0] - m[2];
50 |
51 | var x = d3.scale.ordinal().rangePoints([0, w], 0),
52 | y = {},
53 | dragging = {};
54 |
55 | var line = d3.svg.line(),
56 | axis = d3.svg.axis().orient("left"),
57 | background,
58 | foreground;
59 |
60 | var svg = selection
61 | .attr("width", +width())
62 | .attr("height", +height())
63 | .append("g")
64 | .attr("width", w + m[1] + m[3])
65 | .attr("height", h + m[0] + m[2])
66 | .style("font-size","10px")
67 | .style("font-family","Arial, Helvetica")
68 | .append("svg:g")
69 | .attr("transform", "translate(" + m[3] + "," + m[0] + ")");
70 |
71 | x.domain(dimensions = d3.keys(data[0].dimensions).filter(function (d) {
72 | return d != "name" && (y[d] = d3.scale.linear()
73 | .domain(d3.extent(data, function(p) { return +p.dimensions[d]; }))
74 | .range([h, 0]));
75 | }));
76 |
77 | colors.domain(data, function (d){ return d.color; });
78 |
79 | background = svg.append("svg:g")
80 | .attr("class", "background")
81 | .selectAll("path")
82 | .data(data)
83 | .enter().append("svg:path")
84 | .style('fill','none')
85 | .style('stroke', function (d){ return colors()(d.color); })
86 | .style('stroke-opacity','.4')
87 | .attr("d", path);
88 |
89 | var g = svg.selectAll(".dim")
90 | .data(dimensions)
91 | .enter().append("svg:g")
92 | .attr("class", "dim")
93 | .attr("transform", function(d) { return "translate(" + x(d) + ")"; });
94 |
95 | g.append("svg:g")
96 | .attr("class", "axis")
97 | .each(function(d) { d3.select(this).call(axis.scale(y[d])); })
98 | .append("svg:text")
99 | .attr("text-anchor", "middle")
100 | .style("font-size","10px")
101 | .style("font-family","Arial, Helvetica")
102 | .attr("y", -9)
103 | .text(String);
104 |
105 | d3.selectAll('text')
106 | .style("font-size","10px")
107 | .style("font-family","Arial, Helvetica")
108 |
109 | d3.selectAll(".axis line, .axis path")
110 | .style('fill', 'none')
111 | .style('stroke', '#000')
112 | .style('stroke-width', '1px')
113 | .style('shape-rendering','crispEdges')
114 |
115 | function position(d) {
116 | var v = dragging[d];
117 | return v == null ? x(d) : v;
118 | }
119 |
120 | function path(d) {
121 | return line(dimensions.map(function(p) { return [position(p), y[p](d.dimensions[p])]; }));
122 | }
123 |
124 | });
125 |
126 | })();
--------------------------------------------------------------------------------
/charts/scatterPlot.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var points = raw.models.points();
4 |
5 | var chart = raw.chart()
6 | .title('Scatter Plot')
7 | .description(
8 | "A scatter plot, scatterplot, or scattergraph is a type of mathematical diagram using Cartesian coordinates to display values for two variables for a set of data. The data is displayed as a collection of points, each having the value of one variable determining the position on the horizontal axis and the value of the other variable determining the position on the vertical axis. This kind of plot is also called a scatter chart, scattergram, scatter diagram, or scatter graph.")
9 | .thumbnail("imgs/scatterPlot.png")
10 | .category('Distributions')
11 | .model(points)
12 |
13 | var width = chart.number()
14 | .title("Width")
15 | .defaultValue(1000)
16 | .fitToWidth(true)
17 |
18 | var height = chart.number()
19 | .title("Height")
20 | .defaultValue(500)
21 |
22 | var maxRadius = chart.number()
23 | .title("max radius")
24 | .defaultValue(20)
25 |
26 | var useZero = chart.checkbox()
27 | .title("set origin at (0,0)")
28 | .defaultValue(false)
29 |
30 | var colors = chart.color()
31 | .title("Color scale")
32 |
33 | var showPoints = chart.checkbox()
34 | .title("show points")
35 | .defaultValue(true)
36 |
37 | chart.draw(function (selection, data){
38 |
39 | // Retrieving dimensions from model
40 | var x = points.dimensions().get('x'),
41 | y = points.dimensions().get('y');
42 |
43 | var g = selection
44 | .attr("width", +width() )
45 | .attr("height", +height() )
46 | .append("g")
47 |
48 | var marginLeft = d3.max([maxRadius(),(d3.max(data, function (d) { return (Math.log(d.y) / 2.302585092994046) + 1; }) * 9)]),
49 | marginBottom = 20,
50 | w = width() - marginLeft,
51 | h = height() - marginBottom;
52 |
53 | var xExtent = !useZero()? d3.extent(data, function (d){ return d.x; }) : [0, d3.max(data, function (d){ return d.x; })],
54 | yExtent = !useZero()? d3.extent(data, function (d){ return d.y; }) : [0, d3.max(data, function (d){ return d.y; })];
55 |
56 | var xScale = x.type() == "Date"
57 | ? d3.time.scale().range([marginLeft,width()-maxRadius()]).domain(xExtent)
58 | : d3.scale.linear().range([marginLeft,width()-maxRadius()]).domain(xExtent),
59 | yScale = y.type() == "Date"
60 | ? d3.time.scale().range([h-maxRadius(), maxRadius()]).domain(yExtent)
61 | : d3.scale.linear().range([h-maxRadius(), maxRadius()]).domain(yExtent),
62 | sizeScale = d3.scale.linear().range([1, Math.pow(+maxRadius(),2)*Math.PI]).domain([0, d3.max(data, function (d){ return d.size; })]),
63 | xAxis = d3.svg.axis().scale(xScale).tickSize(-h+maxRadius()*2).orient("bottom")//.tickSubdivide(true),
64 | yAxis = d3.svg.axis().scale(yScale).ticks(10).tickSize(-w+maxRadius()).orient("left");
65 |
66 |
67 | g.append("g")
68 | .attr("class", "x axis")
69 | .style("stroke-width", "1px")
70 | .style("font-size","10px")
71 | .style("font-family","Arial, Helvetica")
72 | .attr("transform", "translate(" + 0 + "," + (h-maxRadius()) + ")")
73 | .call(xAxis);
74 |
75 | g.append("g")
76 | .attr("class", "y axis")
77 | .style("stroke-width", "1px")
78 | .style("font-size","10px")
79 | .style("font-family","Arial, Helvetica")
80 | .attr("transform", "translate(" + marginLeft + "," + 0 + ")")
81 | .call(yAxis);
82 |
83 | d3.selectAll(".y.axis line, .x.axis line, .y.axis path, .x.axis path")
84 | .style("shape-rendering","crispEdges")
85 | .style("fill","none")
86 | .style("stroke","#ccc")
87 |
88 | var circle = g.selectAll("g.circle")
89 | .data(data)
90 | .enter().append("g")
91 | .attr("class","circle")
92 |
93 | var point = g.selectAll("g.point")
94 | .data(data)
95 | .enter().append("g")
96 | .attr("class","point")
97 |
98 | colors.domain(data, function(d){ return d.color; });
99 |
100 | circle.append("circle")
101 | .style("fill", function(d) { return colors() ? colors()(d.color) : "#eeeeee"; })
102 | .style("fill-opacity", .9)
103 | .attr("transform", function(d) { return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"; })
104 | .attr("r", function (d){ return Math.sqrt(sizeScale(d.size)/Math.PI); });
105 |
106 | point.append("circle")
107 | .filter(function(){ return showPoints(); })
108 | .style("fill", "#000")
109 | .attr("transform", function(d) { return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"; })
110 | .attr("r", 1);
111 |
112 | circle.append("text")
113 | .attr("transform", function(d) { return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"; })
114 | .attr("text-anchor", "middle")
115 | .style("font-size","10px")
116 | .attr("dy", 15)
117 | .style("font-family","Arial, Helvetica")
118 | .text(function (d){ return d.label? d.label.join(", ") : ""; });
119 |
120 | })
121 |
122 | })();
--------------------------------------------------------------------------------
/charts/smallMultiplesArea.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var stream = raw.model();
4 |
5 | var group = stream.dimension()
6 | .title('Group')
7 | .required(1)
8 |
9 | var date = stream.dimension()
10 | .title('Date')
11 | .types(Date)
12 | .accessor(function (d){ return this.type() == "Date" ? new Date(d) : +d; })
13 | .required(1)
14 |
15 | var size = stream.dimension()
16 | .title('Size')
17 | .types(Number)
18 | .required(1)
19 |
20 | stream.map(function (data){
21 | if (!group()) return [];
22 |
23 | var dates = d3.set(data.map(function (d){ return +date(d); })).values();
24 |
25 | var groups = d3.nest()
26 | .key(group)
27 | .rollup(function (g){
28 | var singles = d3.nest()
29 | .key(function(d){ return +date(d); })
30 | .rollup(function (d){
31 | return {
32 | group : group(d[0]),
33 | date : date(d[0]),
34 | size : size() ? d3.sum(d,size) : d.length
35 | }
36 | })
37 | .map(g);
38 |
39 | return d3.values(singles);
40 | })
41 | .map(data)
42 |
43 | return d3.values(groups).map(function(d){ return d.sort(function(a,b){ return a.date - b.date; }) });
44 |
45 | })
46 |
47 | var chart = raw.chart()
48 | .title('Small Multiples (Area)')
49 | .thumbnail("imgs/smallMultiples.png")
50 | .description("A small multiple is a series of small similar graphics or charts, allowing them to be easily compared. Based on http://bl.ocks.org/mbostock/9490313")
51 | .category('Time Series')
52 | .model(stream)
53 |
54 | var width = chart.number()
55 | .title("Width")
56 | .defaultValue(1000)
57 | .fitToWidth(true)
58 |
59 | var height = chart.number()
60 | .title("Height")
61 | .defaultValue(500)
62 |
63 | var padding = chart.number()
64 | .title("Padding")
65 | .defaultValue(10)
66 |
67 | var scale = chart.checkbox()
68 | .title("Use same scale")
69 | .defaultValue(false)
70 |
71 | var colors = chart.color()
72 | .title("Color scale")
73 |
74 | chart.draw(function (selection, data){
75 |
76 | var w = +width(),
77 | h = (+height() - (+padding()*(data.length-1))) / (data.length);
78 |
79 | var svg = selection
80 | .attr("width", +width())
81 | .attr("height", +height())
82 |
83 | var x = d3.time.scale()
84 | .range([0, w]);
85 |
86 | var y = d3.scale.linear()
87 | .range([h, 0]);
88 |
89 | var area = d3.svg.area()
90 | .x(function(d) { return x(d.date); })
91 | .y0(h)
92 | .y1(function(d) { return y(d.size); });
93 |
94 | var line = d3.svg.line()
95 | .x(function(d) { return x(d.date); })
96 | .y(function(d) { return y(d.size); });
97 |
98 | x.domain([
99 | d3.min(data, function(layer) { return d3.min(layer, function(d) { return d.date; }); }),
100 | d3.max(data, function(layer) { return d3.max(layer, function(d) { return d.date; }); })
101 | ])
102 |
103 | colors.domain(data, function (d){ return d[0].group; })
104 |
105 | svg.selectAll("g")
106 | .data(data)
107 | .enter().append("g")
108 | .attr("title", function(d) { return d[0].group; })
109 | .attr("transform", function(d,i) { return "translate(0," + ((h+padding())*i) + ")"})
110 | .each(multiple);
111 |
112 | svg.selectAll("g")
113 | .append("text")
114 | .attr("x", w - 6)
115 | .attr("y", h - 6)
116 | .style("font-size","10px")
117 | .style("fill", function(d){ return raw.foreground(colors()(d[0].group)) })
118 | .style("font-family","Arial, Helvetica")
119 | .style("text-anchor", "end")
120 | .text(function(d) { return d[0].group; });
121 |
122 | function multiple(single) {
123 |
124 | var g = d3.select(this);
125 |
126 | if (scale()) y.domain([0, d3.max(data, function(layer) { return d3.max(layer, function(d) { return d.size; }); })])
127 | else y.domain([0, d3.max(single, function(d) { return d.size; })]);
128 |
129 | g.append("path")
130 | .attr("class", "area")
131 | .style("fill", function(d){ return colors()(d[0].group); })
132 | .attr("d", area(single));
133 |
134 | /*g.append("path")
135 | .attr("class", "line")
136 | .style("fill","none")
137 | .style("stroke","#666")
138 | .style("stroke-width","1.5px")
139 | .attr("d", line(single));*/
140 |
141 | }
142 |
143 | })
144 |
145 | })();
--------------------------------------------------------------------------------
/js/controllers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* Controllers */
4 |
5 | angular.module('raw.controllers', [])
6 |
7 | .controller('RawCtrl', function ($scope, dataService) {
8 |
9 | $scope.samples = [
10 | { title : 'Cars (multivariate)', url : 'data/multivariate.csv' },
11 | { title : 'Movies (dispersions)', url : 'data/dispersions.csv' },
12 | { title : 'Music (flows)', url : 'data/flows.csv' },
13 | { title : 'Cocktails (correlations)', url : 'data/correlations.csv' }
14 | ]
15 |
16 | $scope.$watch('sample', function (sample){
17 | if (!sample) return;
18 | dataService.loadSample(sample.url).then(
19 | function(data){
20 | $scope.text = data;
21 | },
22 | function(error){
23 | $scope.error = error;
24 | }
25 | );
26 | });
27 |
28 | // init
29 | $scope.raw = raw;
30 | $scope.data = [];
31 | $scope.metadata = [];
32 | $scope.error = false;
33 | $scope.loading = true;
34 |
35 | $scope.categories = ['Correlations', 'Distributions', 'Time Series', 'Hierarchies', 'Others'];
36 |
37 | $scope.parse = function(text){
38 |
39 | if ($scope.model) $scope.model.clear();
40 |
41 | $scope.data = [];
42 | $scope.metadata = [];
43 | $scope.error = false;
44 | $scope.$apply();
45 |
46 | try {
47 | var parser = raw.parser();
48 | $scope.data = parser(text);
49 | $scope.metadata = parser.metadata(text);
50 | $scope.error = false;
51 | } catch(e){
52 | $scope.data = [];
53 | $scope.metadata = [];
54 | $scope.error = e.name == "ParseError" ? +e.message : false;
55 | }
56 | if (!$scope.data.length && $scope.model) $scope.model.clear();
57 | $scope.loading = false;
58 | }
59 |
60 | $scope.delayParse = dataService.debounce($scope.parse, 500, false);
61 |
62 | $scope.$watch("text", function (text){
63 | $scope.loading = true;
64 | $scope.delayParse(text);
65 | });
66 |
67 | $scope.charts = raw.charts.values().sort(function (a,b){ return a.title() < b.title() ? -1 : a.title() > b.title() ? 1 : 0; });
68 | $scope.chart = $scope.charts[0];
69 | $scope.model = $scope.chart ? $scope.chart.model() : null;
70 |
71 | $scope.$watch('error', function (error){
72 | if (!$('.CodeMirror')[0]) return;
73 | var cm = $('.CodeMirror')[0].CodeMirror;
74 | if (!error) {
75 | cm.removeLineClass($scope.lastError,'wrap','line-error');
76 | return;
77 | }
78 | cm.addLineClass(error, 'wrap', 'line-error');
79 | cm.scrollIntoView(error);
80 | $scope.lastError = error;
81 |
82 | })
83 |
84 | $('body').mousedown(function (e,ui){
85 | if ($(e.target).hasClass("dimension-info-toggle")) return;
86 | $('.dimensions-wrapper').each(function (e){
87 | angular.element(this).scope().open = false;
88 | angular.element(this).scope().$apply();
89 | })
90 | })
91 |
92 | $scope.codeMirrorOptions = {
93 | lineNumbers : true,
94 | lineWrapping : true,
95 | placeholder : 'Paste your text or drop a file here. No data on hand? Try one of our sample datasets!'
96 | }
97 |
98 | $scope.selectChart = function(chart){
99 | if (chart == $scope.chart) return;
100 | $scope.model.clear();
101 | $scope.chart = chart;
102 | $scope.model = $scope.chart.model();
103 | }
104 |
105 | function refreshScroll(){
106 | $('[data-spy="scroll"]').each(function () {
107 | $(this).scrollspy('refresh');
108 | });
109 | }
110 |
111 | $(window).scroll(function(){
112 |
113 | // check for mobile
114 | if ($(window).width() < 760 || $('#mapping').height() < 300) return;
115 |
116 | var scrollTop = $(window).scrollTop() + 0,
117 | mappingTop = $('#mapping').offset().top + 10,
118 | mappingHeight = $('#mapping').height(),
119 | isBetween = scrollTop > mappingTop + 50 && scrollTop <= mappingTop + mappingHeight - $(".sticky").height() - 20,
120 | isOver = scrollTop > mappingTop + mappingHeight - $(".sticky").height() - 20,
121 | mappingWidth = mappingWidth ? mappingWidth : $('.col-lg-9').width();
122 |
123 | if (mappingHeight-$('.dimensions-list').height() > 90) return;
124 | //console.log(mappingHeight-$('.dimensions-list').height())
125 | if (isBetween) {
126 | $(".sticky")
127 | .css("position","fixed")
128 | .css("width", mappingWidth+"px")
129 | .css("top","20px")
130 | }
131 |
132 | if(isOver) {
133 | $(".sticky")
134 | .css("position","fixed")
135 | .css("width", mappingWidth+"px")
136 | .css("top", (mappingHeight - $(".sticky").height() + 0 - scrollTop+mappingTop) + "px");
137 | return;
138 | }
139 |
140 | if (isBetween) return;
141 |
142 | $(".sticky")
143 | .css("position","relative")
144 | .css("top","")
145 | .css("width", "");
146 |
147 | })
148 |
149 | $(document).ready(refreshScroll);
150 |
151 |
152 | })
--------------------------------------------------------------------------------
/charts/alluvial.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var graph = raw.models.graph();
4 |
5 | var chart = raw.chart()
6 | .title('Alluvial Diagram')
7 | .description(
8 | "Alluvial diagrams allow to represent flows and to see correlations between categorical dimensions, visually linking to the number of elements sharing the same categories. It is useful to see the evolution of cluster (such as the number of people belonging to a specific group). It can also be used to represent bipartite graphs, using each node group as dimensions. Mainly based on our previous work with Fineo, it is inspired by http://bost.ocks.org/mike/sankey/")
9 | .thumbnail("imgs/alluvial.png")
10 | .category("Correlations")
11 | .model(graph)
12 |
13 | var width = chart.number()
14 | .title("Width")
15 | .defaultValue(1000)
16 | .fitToWidth(true)
17 |
18 | var height = chart.number()
19 | .title("Height")
20 | .defaultValue(500)
21 |
22 | var nodeWidth = chart.number()
23 | .title("Node Width")
24 | .defaultValue(5)
25 |
26 | var sortBy = chart.list()
27 | .title("Sort by")
28 | .values(['size','name','automatic'])
29 | .defaultValue('size')
30 |
31 | var colors = chart.color()
32 | .title("Color scale")
33 |
34 | chart.draw(function (selection, data){
35 |
36 | var formatNumber = d3.format(",.0f"),
37 | format = function(d) { return formatNumber(d); };
38 |
39 | var g = selection
40 | .attr("width", +width() )
41 | .attr("height", +height() + 20 )
42 | .append("g")
43 | .attr("transform", "translate(" + 0 + "," + 10 + ")");
44 |
45 | // Calculating the best nodePadding
46 |
47 | var nested = d3.nest()
48 | .key(function (d){ return d.group; })
49 | .rollup(function (d){ return d.length; })
50 | .entries(data.nodes)
51 |
52 | var maxNodes = d3.max(nested, function (d){ return d.values; });
53 |
54 | var sankey = d3.sankey()
55 | .nodeWidth(+nodeWidth())
56 | .nodePadding(d3.min([10,(height()-maxNodes)/maxNodes]))
57 | .size([+width(), +height()]);
58 |
59 | var path = sankey.link(),
60 | nodes = data.nodes,
61 | links = data.links;
62 |
63 | sankey
64 | .nodes(nodes)
65 | .links(links)
66 | .layout(32);
67 |
68 | // Re-sorting nodes
69 |
70 | nested = d3.nest()
71 | .key(function(d){ return d.group; })
72 | .map(nodes)
73 |
74 | d3.values(nested)
75 | .forEach(function (d){
76 | var y = ( height() - d3.sum(d,function(n){ return n.dy+sankey.nodePadding();}) ) / 2 + sankey.nodePadding()/2;
77 | d.sort(function (a,b){
78 | if (sortBy() == "automatic") return b.y - a.y;
79 | if (sortBy() == "size") return b.dy - a.dy;
80 | if (sortBy() == "name") return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
81 | })
82 | d.forEach(function (node){
83 | node.y = y;
84 | y += node.dy +sankey.nodePadding();
85 | })
86 | })
87 |
88 | // Resorting links
89 |
90 | d3.values(nested).forEach(function (d){
91 |
92 | d.forEach(function (node){
93 |
94 | var ly = 0;
95 | node.sourceLinks
96 | .sort(function (a,b){
97 | return a.target.y - b.target.y;
98 | })
99 | .forEach(function (link){
100 | link.sy = ly;
101 | ly += link.dy;
102 | })
103 |
104 | ly = 0;
105 |
106 | node.targetLinks
107 | .sort(function(a,b){
108 | return a.source.y - b.source.y;
109 | })
110 | .forEach(function (link){
111 | link.ty = ly;
112 | ly += link.dy;
113 | })
114 | })
115 | })
116 |
117 | colors.domain(links, function (d){ return d.source.name; });
118 |
119 | var link = g.append("g").selectAll(".link")
120 | .data(links)
121 | .enter().append("path")
122 | .attr("class", "link")
123 | .attr("d", path )
124 | .style("stroke-width", function(d) { return Math.max(1, d.dy); })
125 | .style("fill","none")
126 | .style("stroke", function (d){ return colors()(d.source.name); })
127 | .style("stroke-opacity",".4")
128 | .sort(function(a, b) { return b.dy - a.dy; });
129 |
130 | var node = g.append("g").selectAll(".node")
131 | .data(nodes)
132 | .enter().append("g")
133 | .attr("class", "node")
134 | .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
135 |
136 | node.append("rect")
137 | .attr("height", function(d) { return d.dy; })
138 | .attr("width", sankey.nodeWidth())
139 | .style("fill", function (d) { return d.sourceLinks.length ? colors(d.name) : "#666"; })
140 | .append("title")
141 | .text(function(d) { return d.name + "\n" + format(d.value); });
142 |
143 | node.append("text")
144 | .attr("x", -6)
145 | .attr("y", function (d) { return d.dy / 2; })
146 | .attr("dy", ".35em")
147 | .attr("text-anchor", "end")
148 | .attr("transform", null)
149 | .text(function(d) { return d.name; })
150 | .style("font-size","11px")
151 | .style("font-family","Arial, Helvetica")
152 | .style("pointer-events","none")
153 | .filter(function(d) { return d.x < +width() / 2; })
154 | .attr("x", 6 + sankey.nodeWidth())
155 | .attr("text-anchor", "start");
156 |
157 | })
158 |
159 | })();
--------------------------------------------------------------------------------
/charts/clusterForce.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var nodes = raw.model();
4 |
5 | var cluster = nodes.dimension()
6 | .title("Clusters")
7 | .required(1);
8 |
9 | var size = nodes.dimension()
10 | .title("Size")
11 | .types(Number)
12 |
13 | var label = nodes.dimension()
14 | .title("Label")
15 |
16 | var color = nodes.dimension()
17 | .title("Color")
18 |
19 | nodes.map(function (data){
20 |
21 | var nodeClusters = d3.nest()
22 | .key(function (d) { return cluster(d); })
23 | .rollup(function (d){
24 | return {
25 | type: 'cluster',
26 | cluster: cluster(d[0]),
27 | size: 0,
28 | }
29 | })
30 | .map(data);
31 |
32 | var nodeElements = data.map(function (d) {
33 | return {
34 | type : 'node',
35 | label : label(d),
36 | cluster: cluster(d),
37 | clusterObject : nodeClusters[cluster(d)],
38 | size: size() ? +size(d) : 1,
39 | color: color(d)
40 | };
41 | });
42 |
43 | return nodeElements.concat(d3.values(nodeClusters));
44 |
45 | })
46 |
47 |
48 | var chart = raw.chart()
49 | .title('Clustered Force Layout')
50 | .description(
51 | "Nested circles allow to represent hierarchies and compare values. This visualization is particularly effective to show the proportion between elements through their areas and their position inside a hierarchical structure. Based on http://bl.ocks.org/mbostock/7882658")
52 | .thumbnail("imgs/clusterForce.png")
53 | .model(nodes)
54 |
55 | var width = chart.number()
56 | .title("Width")
57 | .defaultValue(1000)
58 | .fitToWidth(true)
59 |
60 | var height = chart.number()
61 | .title("Height")
62 | .defaultValue(500)
63 |
64 | var nodePadding = chart.number()
65 | .title("node padding")
66 | .defaultValue(2)
67 |
68 | var clusterPadding = chart.number()
69 | .title("cluster padding")
70 | .defaultValue(10)
71 |
72 | var colors = chart.color()
73 | .title("Color scale")
74 |
75 | chart.draw(function (selection, data){
76 |
77 | d3.layout.pack()
78 | .sort(null)
79 | .size([+width(), +height()])
80 | .padding(d3.max([nodePadding(),clusterPadding()]))
81 | .children(function (d) { return d.values; })
82 | .value(function (d) { return +d.size; })
83 | .nodes({
84 | values: d3.nest()
85 | .key(function (d) { return d.cluster; })
86 | .entries(data)
87 | }
88 | );
89 |
90 | var force = d3.layout.force()
91 | .nodes(data)
92 | .size([+width(), +height()])
93 | .gravity(.01)
94 | .charge(0)
95 | .on("tick", tick)
96 | .start();
97 |
98 | var g = selection
99 | .attr("width", width)
100 | .attr("height", height);
101 |
102 | colors.domain(data.filter(function (d){ return d.type == "node"; }), function (d){ return d.color; });
103 |
104 | var node = g.selectAll("circle")
105 | .data(data.filter(function (d){ return d.type == "node"; }))
106 | .enter().append("circle")
107 | .style("fill", function(d) { return d.color ? colors()(d.color) : colors()(null); })
108 | .call(force.drag);
109 |
110 | node.transition()
111 | .delay(function(d, i) { return i * 5; })
112 | .attrTween("r", function(d) {
113 | var i = d3.interpolate(0, +d.r);
114 | return function(t) { return d.radius = i(t); };
115 | });
116 |
117 | var text = g.selectAll("text")
118 | .data(data.filter(function (d){ return d.type == "node"; }))
119 | .enter().append("text")
120 | .text(function (d){ return d.label; })
121 | .attr("text-anchor", "middle")
122 | .attr("dy","4")
123 | .style("font-size","11px")
124 | .style("font-family","Arial, Helvetica")
125 | .call(force.drag);
126 |
127 | function tick(e) {
128 | node
129 | .each(cluster(10 * e.alpha * e.alpha))
130 | .each(collide(.5))
131 | .attr("cx", function(d) { return d.x; })
132 | .attr("cy", function(d) { return d.y; });
133 | text
134 | .each(cluster(10 * e.alpha * e.alpha))
135 | .each(collide(.5))
136 | .attr("x", function(d) { return d.x; })
137 | .attr("y", function(d) { return d.y; });
138 | }
139 |
140 | function cluster(alpha) {
141 | return function(d) {
142 | if (d.type != "node") return;
143 | var cluster = d.clusterObject;
144 | var x = d.x - cluster.x,
145 | y = d.y - cluster.y,
146 | l = Math.sqrt(x * x + y * y),
147 | r = d.r + cluster.r;
148 | if (l != r) {
149 | l = (l - r) / l * alpha;
150 | d.x -= x *= l;
151 | d.y -= y *= l;
152 | cluster.x += x;
153 | cluster.y += y;
154 | }
155 | };
156 | }
157 |
158 | function collide(alpha) {
159 | var quadtree = d3.geom.quadtree(data);
160 | return function(d) {
161 | var r = d.r + Math.max(+nodePadding(), +clusterPadding()),
162 | nx1 = d.x - r,
163 | nx2 = d.x + r,
164 | ny1 = d.y - r,
165 | ny2 = d.y + r;
166 |
167 | quadtree.visit(function(quad, x1, y1, x2, y2) {
168 | if (quad.point && (quad.point !== d)) {
169 | var x = d.x - quad.point.x,
170 | y = d.y - quad.point.y,
171 | l = Math.sqrt(x * x + y * y),
172 | r = d.r + quad.point.radius + (d.cluster === quad.point.cluster ? +nodePadding() : +clusterPadding());
173 | if (l < r) {
174 | l = (l - r) / l * alpha;
175 | d.x -= x *= l;
176 | d.y -= y *= l;
177 | quad.point.x += x;
178 | quad.point.y += y;
179 | }
180 | }
181 | return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
182 | });
183 | };
184 | }
185 |
186 | })
187 |
188 | })();
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ##About
5 |
6 | **RAW** is an open web tool developed at the [DensityDesign Research Lab](http://www.densitydesign.org) (Politecnico di Milano) to create custom vector-based visualizations on top of the amazing [d3.js](https://github.com/mbostock/d3) library by [Mike Bostock](http://bost.ocks.org/mike/).
7 | Primarily conceived as a tool for designers and vis geeks, RAW aims at providing a missing link between spreadsheet applications (e.g. Microsoft Excel, Apple Numbers, Google Docs, OpenRefine, …) and vector graphics editors (e.g. Adobe Illustrator, Inkscape, …).
8 |
9 | RAW works with [delimiter-separated values](http://en.wikipedia.org/wiki/Delimiter-separated_values) (i.e. csv and tsv files) as well as with copied-and-pasted texts from other applications (e.g. Microsoft Excel, TextWrangler, TextEdit, …). Based on the [svg](http://en.wikipedia.org/wiki/Svg) format, visualizations can be easily edited with vector graphics applications for further refinements, or directly embedded into web pages.
10 |
11 | Knowing the need of working with sensitive information, the data uploaded to RAW is processed only by the web browser: **no server-side operations or storages are performed** and no one will see, touch or copy your data!
12 |
13 | RAW is also highly customizable and extensible, accepting new custom layouts defined by users. For more information about how to add or edit layouts, see the [Developer Guide](https://github.com/densitydesign/raw/wiki/Developer-Guide).
14 |
15 | - App page: [app.raw.densitydesign.org](http://app.raw.densitydesign.org)
16 | - Project official page: [raw.densitydesign.org](http://raw.densitydesign.org)
17 | - Documentation: [github.com/densitydesign/raw/wiki](https://github.com/densitydesign/raw/wiki)
18 | - Google group: [groups.google.com/forum/#!forum/densitydesign-raw](https://groups.google.com/forum/#!forum/densitydesign-raw)
19 |
20 |
21 | ##Usage
22 | The easiest way to use RAW is by accessing the most updated version on the **[official app page](http://app.raw.densitydesign.org)**. However, RAW can also run locally on your machine: see the installation instructions below to know how.
23 |
24 | ##Installation
25 | If you want to run your instance of RAW locally on your machine, be sure you have the following requirements installed.
26 |
27 | ###Requirements
28 |
29 | - [git](http://git-scm.com/book/en/Getting-Started-Installing-Git)
30 | - [Bower](http://bower.io/#installing-bower)
31 |
32 | ###Instructions
33 |
34 | Clone RAW from the command line:
35 |
36 | ``` sh
37 | $ git clone git://github.com/densitydesign/raw.git
38 | ```
39 |
40 | browse to RAW root folder:
41 |
42 | ``` sh
43 | $ cd raw
44 | ```
45 |
46 | install client-side dependencies:
47 |
48 | ``` sh
49 | $ bower install
50 | ```
51 |
52 | You can now run RAW from your local web server. For example, you can run Python's built-in server:
53 |
54 | ``` sh
55 | $ python -m SimpleHTTPServer 4000
56 | ```
57 |
58 | or for Python 3+
59 |
60 | ``` sh
61 | $ python -m http.server 4000
62 | ```
63 |
64 | Once this is running, go to [http://localhost:4000/](http://localhost:4000/).
65 |
66 | Troubles with the installation? Maybe a look at the [issues](https://github.com/densitydesign/raw/issues) page can solve your problem, otherwise join the [Google group](https://groups.google.com/forum/#!forum/densitydesign-raw).
67 |
68 |
69 | ##Documentation and Support
70 |
71 | Documentation and FAQs about how to use RAW can be found on the [wiki](https://github.com/densitydesign/raw/wiki/).
72 |
73 | ##Charts
74 |
75 | Information about the available charts can be found [here](https://github.com/densitydesign/raw/wiki/Available-Charts). Adding new charts is very easy in RAW, see how [here](https://github.com/densitydesign/raw/wiki/Adding-New-Charts)!
76 |
77 | If you have any suggestion or request about new layouts to include, please let us know! If you have already created new charts and you would like to see them included into Raw, please send us a [pull request](https://github.com/densitydesign/raw/pulls).
78 |
79 | ##Libraries
80 |
81 | **RAW** has been developed using a lot of cool stuff found out there:
82 |
83 | [angular.js](https://github.com/angular/angular.js)
84 | [angular-bootstrap-colorpicker](https://github.com/buberdds/angular-bootstrap-colorpicker)
85 | [angular-ui](https://github.com/angular-ui)
86 | [bootstrap](https://github.com/twbs/bootstrap)
87 | [bootstrap-colorpicker](http://www.eyecon.ro/bootstrap-colorpicker/)
88 | [Bower](https://github.com/bower/bower)
89 | [canvas-toBlob.js](https://github.com/eligrey/canvas-toBlob.js)
90 | [CodeMirror](https://github.com/marijnh/codemirror)
91 | [d3.js](https://github.com/mbostock/d3)
92 | [FileSaver.js](https://github.com/eligrey/FileSaver.js)
93 | [jQuery](https://github.com/jquery/jquery)
94 | [jQuery UI Touch Punch](https://github.com/furf/jquery-ui-touch-punch/)
95 | [ZeroClipboard](https://github.com/zeroclipboard/zeroclipboard)
96 |
97 | ##Roadmap
98 |
99 | - ~~Refactoring using [reusable charts](http://bost.ocks.org/mike/chart/) as layouts~~
100 | - ~~Introducing continuous color scales (for numeric values)~~
101 | - ~~Mobile support~~
102 | - Improving documentation and API Reference
103 | - Creating and exporting legends
104 | - PDF export
105 |
106 | ##Team and Contacts
107 |
108 | **RAW** has been developed and maintained at DensityDesign Research Lab by:
109 |
110 | Giorgio Caviglia
111 | Michele Mauri
112 | Giorgio Uboldi
113 | Matteo Azzi
114 |
115 | If you want to know more about RAW, how it works and future developments, please visit the [official website](http://raw.densitydesign.org). For any specific request or comment we suggest you to use Github or the [Google group](https://groups.google.com/forum/#!forum/densitydesign-raw). If none of these worked for you, you can write us at .
116 |
117 | ##Contributing
118 |
119 | Want to contribute to RAW's development? You are more than welcome! Start by forking the repository (the "Fork" button at the top-right corner of this page) and follow the instructions above to clone it and install dependencies. Then you can use Github's issues and pull requests to discuss and share your work.
120 |
121 |
122 | ##License
123 |
124 | RAW is provided under the [LGPL (Lesser General Public License)](https://github.com/densitydesign/raw/blob/master/COPYING.LESSER) v.3:
125 |
126 | Copyright (c), 2013-2014 DensityDesign Lab, Giorgio Caviglia, Michele Mauri,
127 | Giorgio Uboldi, Matteo Azzi
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | This program is free software: you can redistribute it and/or modify
136 | it under the terms of the GNU Lesser General Public License as published by
137 | the Free Software Foundation, either version 3 of the License, or
138 | (at your option) any later version.
139 |
140 | This program is distributed in the hope that it will be useful,
141 | but WITHOUT ANY WARRANTY; without even the implied warranty of
142 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
143 | GNU Lesser General Public License for more details.
144 |
145 | You should have received a copy of the GNU Lesser General Public License
146 | along with this program. If not, see .
147 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | RAW
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
54 |
55 |
56 |
57 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/COPYING.LESSER:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
--------------------------------------------------------------------------------
/charts/streamgraph.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var stream = raw.model();
4 |
5 | var group = stream.dimension()
6 | .title('Group')
7 | .required(1)
8 |
9 | var date = stream.dimension()
10 | .title('Date')
11 | .types(Number, Date, String)
12 | .accessor(function (d){ return this.type() == "Date" ? new Date(d) : this.type() == "String" ? d : +d; })
13 | .required(1)
14 |
15 | var size = stream.dimension()
16 | .title('Size')
17 | .types(Number)
18 | .required(1)
19 |
20 | stream.map(function (data){
21 | if (!group()) return [];
22 |
23 | var dates = d3.set(data.map(function (d){ return date(d); })).values();
24 |
25 | var groups = d3.nest()
26 | .key(group)
27 | .rollup(function (g){
28 |
29 | var singles = d3.nest()
30 | .key(function(d){ return date(d); })
31 | .rollup(function (d){
32 | return {
33 | group : group(d[0]),
34 | x : date(d[0]),
35 | y : size() ? d3.sum(d,size) : d.length
36 | }
37 | })
38 | .map(g);
39 |
40 | // let's create the empty ones
41 | dates.forEach(function(d){
42 | if (!singles.hasOwnProperty(d)) {
43 | //console.log(singles, d);
44 | singles[d] = { group : group(g[0]), x : d, y : 0 }
45 | }
46 | })
47 |
48 | return d3.values(singles);
49 | })
50 | .map(data)
51 |
52 | return d3.values(groups).map(function(d){ return d.sort(function(a,b){ return a.x - b.x; }) });
53 |
54 | })
55 |
56 | var chart = raw.chart()
57 | .title('Streamgraph')
58 | .thumbnail("imgs/streamgraph.png")
59 | .description(
60 | "For continuous data such as time series, a streamgraph can be used in place of stacked bars. Based on http://bl.ocks.org/mbostock/4060954")
61 | .category('Time Series')
62 | .model(stream)
63 |
64 | var width = chart.number()
65 | .title("Width")
66 | .defaultValue(1000)
67 | .fitToWidth(true)
68 |
69 | var height = chart.number()
70 | .title("Height")
71 | .defaultValue(500)
72 |
73 | var offset = chart.list()
74 | .title("Offset")
75 | .values(['silhouette','wiggle','expand','zero'])
76 | .defaultValue('silhouette')
77 |
78 | var curve = chart.list()
79 | .title("Interpolation")
80 | .values(['Sankey curves','Linear'])
81 | .defaultValue('Sankey curves')
82 |
83 | var showLabels = chart.checkbox()
84 | .title("show labels")
85 | .defaultValue(true)
86 |
87 | var colors = chart.color()
88 | .title("Color scale")
89 |
90 | chart.draw(function (selection, data){
91 |
92 | var g = selection
93 | .attr("width", +width() )
94 | .attr("height", +height() )
95 | .append("g")
96 |
97 | var curves = {
98 | 'Sankey curves' : interpolate,
99 | 'Linear' : 'linear'
100 | }
101 |
102 | var stack = d3.layout.stack()
103 | .offset(offset());
104 |
105 | var layers = stack(data);
106 |
107 | var x = date.type() == "Date"
108 | // Date
109 | ? d3.time.scale()
110 | .domain( [ d3.min(layers, function(layer) { return d3.min(layer, function(d) { return d.x; }); }), d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.x; }); }) ])
111 | .range([0, +width()])
112 | : date && date.type() == "String"
113 | // String
114 | ? d3.scale.ordinal()
115 | .domain(layers[0].map(function(d){ return d.x; }) )
116 | .rangePoints([0, +width()],0)
117 | // Number
118 | : d3.scale.linear()
119 | .domain( [ d3.min(layers, function(layer) { return d3.min(layer, function(d) { return d.x; }); }), d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.x; }); }) ])
120 | .range([0, +width()]);
121 |
122 | var y = d3.scale.linear()
123 | .domain([0, d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); })])
124 | .range([+height()-20, 0]);
125 |
126 | var xAxis = d3.svg.axis().scale(x).tickSize(-height()+20).orient("bottom")//.tickFormat(d3.format("d"))
127 |
128 | g.append("g")
129 | .attr("class", "x axis")
130 | .style("stroke-width", "1px")
131 | .style("font-size","10px")
132 | .style("font-family","Arial, Helvetica")
133 | .attr("transform", "translate(" + 0 + "," + (height()-20) + ")")
134 | .call(xAxis);
135 |
136 | d3.selectAll(".x.axis line, .x.axis path")
137 | .style("shape-rendering","crispEdges")
138 | .style("fill","none")
139 | .style("stroke","#ccc")
140 |
141 | colors.domain(layers, function (d){ return d[0].group; })
142 |
143 | var area = d3.svg.area()
144 | .interpolate(curves[curve()])
145 | .x(function(d) { return x(d.x); })
146 | .y0(function(d) { return y(d.y0); })
147 | .y1(function(d) { return y(d.y0 + d.y); });
148 |
149 | var line = d3.svg.line()
150 | .interpolate(curves[curve()])
151 | .x(function(d) { return x(d.x); })
152 | .y(function(d) {
153 | var y0 = y(d.y0), y1 = y(d.y0 + d.y);
154 | return y0 + (y1 - y0) * 0.5;
155 | });
156 |
157 |
158 | g.selectAll("path.layer")
159 | .data(layers)
160 | .enter().append("path")
161 | .attr("class","layer")
162 | .attr("d", area)
163 | .attr("title", function (d){ return d[0].group; })
164 | .style("fill", function (d) { return colors()(d[0].group); });
165 |
166 | if (!showLabels()) return;
167 |
168 | g.append('defs');
169 |
170 | g.select('defs')
171 | .selectAll('path')
172 | .data(layers)
173 | .enter().append('path')
174 | .attr('id', function(d,i) { return 'path-' + i; })
175 | .attr('d', line);
176 |
177 | g.selectAll("text.label")
178 | .data(layers)
179 | .enter().append('text')
180 | .attr('dy', '0.5ex')
181 | .attr("class","label")
182 | .append('textPath')
183 | .attr('xlink:xlink:href', function(d,i) { return '#path-' + i; })
184 | .attr('startOffset', function(d) {
185 | var maxYr = 0, maxV = 0;
186 | d3.range(layers[0].length).forEach(function(i) {
187 | if (d[i].y > maxV) {
188 | maxV = d[i].y;
189 | maxYr = i;
190 | }
191 | });
192 | d.maxVal = d[maxYr].y;
193 | d.offset = Math.round(x(d[maxYr].x) / x.range()[1] * 100);
194 | return Math.min(95, Math.max(5, d.offset))+'%';
195 | })
196 | .attr('text-anchor', function(d) {
197 | return d.offset > 90 ? 'end' : d.offset < 10 ? 'start' : 'middle';
198 | })
199 | .text(function(d){ return d[0].group; })
200 | .style("font-size","11px")
201 | .style("font-family","Arial, Helvetica")
202 | .style("font-weight","normal")
203 |
204 | /*g.selectAll("text.label")
205 | .data(labels(layers))
206 | .enter().append("text")
207 | .attr("x", function(d){ return x(d.x); })
208 | .attr("y", function(d){ return y(d.y0 + d.y/2); })
209 | .text(function(d){ return d.group; })
210 | .style("font-size","11px")
211 | .style("font-family","Arial, Helvetica")
212 |
213 |
214 |
215 | function labels(layers){
216 | return layers.map(function(layer){
217 | var l = layer[0], max = 0;
218 | layer.forEach(function(d){
219 | if ( d.y > max ) {
220 | max = d.y;
221 | l = d;
222 | }
223 | })
224 | return l;
225 | })
226 |
227 | }*/
228 |
229 | function interpolate(points) {
230 | var x0 = points[0][0], y0 = points[0][1], x1, y1, x2,
231 | path = [x0, ",", y0],
232 | i = 0,
233 | n = points.length;
234 |
235 | while (++i < n) {
236 | x1 = points[i][0], y1 = points[i][1], x2 = (x0 + x1) / 2;
237 | path.push("C", x2, ",", y0, " ", x2, ",", y1, " ", x1, ",", y1);
238 | x0 = x1, y0 = y1;
239 | }
240 | return path.join("");
241 | }
242 |
243 | })
244 |
245 | })();
--------------------------------------------------------------------------------
/charts/bumpChart.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var stream = raw.model();
4 |
5 | var group = stream.dimension()
6 | .title('Group')
7 | .required(1)
8 |
9 | var date = stream.dimension()
10 | .title('Date')
11 | .types(Number, Date, String)
12 | .accessor(function (d){ return this.type() == "Date" ? new Date(d) : this.type() == "String" ? d : +d; })
13 | .required(1)
14 |
15 | var size = stream.dimension()
16 | .title('Size')
17 | .types(Number)
18 | .required(1)
19 |
20 | stream.map(function (data){
21 | if (!group()) return [];
22 |
23 | var dates = d3.set(data.map(function (d){ return date(d); })).values();
24 |
25 | var groups = d3.nest()
26 | .key(group)
27 | .rollup(function (g){
28 |
29 | var singles = d3.nest()
30 | .key(function(d){ return date(d); })
31 | .rollup(function (d){
32 | return {
33 | group : group(d[0]),
34 | x : date(d[0]),
35 | y : size() ? d3.sum(d,size) : d.length
36 | }
37 | })
38 | .map(g);
39 |
40 | // let's create the empty ones
41 | dates.forEach(function(d){
42 | if (!singles.hasOwnProperty(d)) {
43 | singles[d] = { group : group(g[0]), x : d, y : 0 }
44 | }
45 | })
46 |
47 | return d3.values(singles);
48 | })
49 | .map(data)
50 |
51 | return d3.values(groups).map(function(d){ return d.sort(function(a,b){ return a.x - b.x; }) });
52 |
53 | })
54 |
55 | var chart = raw.chart()
56 | .title('Bump Chart')
57 | .thumbnail("imgs/bumpChart.png")
58 | .description(
59 | "For continuous data such as time series, a bump chart can be used in place of stacked bars. Based on New York Times's interactive visualization.")
60 | .category('Time Series')
61 | .model(stream)
62 |
63 | var width = chart.number()
64 | .title("Width")
65 | .defaultValue(1000)
66 | .fitToWidth(true)
67 |
68 | var height = chart.number()
69 | .title("Height")
70 | .defaultValue(500)
71 |
72 | var padding = chart.number()
73 | .title("Padding")
74 | .defaultValue(1)
75 |
76 | var normalize = chart.checkbox()
77 | .title("normalize")
78 | .defaultValue(false)
79 |
80 | var curve = chart.list()
81 | .title("Interpolation")
82 | .values(['Basis spline','Sankey','Linear'])
83 | .defaultValue('Sankey')
84 |
85 | var sort = chart.list()
86 | .title("sort by")
87 | .values(['value (descending)', 'value (ascending)', 'group'])
88 | .defaultValue('value (descending)')
89 |
90 | var showLabels = chart.checkbox()
91 | .title("show labels")
92 | .defaultValue(true)
93 |
94 | var colors = chart.color()
95 | .title("Color scale")
96 |
97 | chart.draw(function (selection, data){
98 |
99 | var g = selection
100 | .attr("width", +width() )
101 | .attr("xmlns:xmlns:xlink", "http://www.w3.org/1999/xlink")
102 | .attr("height", +height() )
103 | .append("g")
104 |
105 | var layers = data;
106 |
107 | var curves = {
108 | 'Basis spline' : 'basis',
109 | 'Sankey' : interpolate,
110 | 'Linear' : 'linear'
111 | }
112 |
113 | layers[0].forEach(function(d,i){
114 |
115 | var values = layers.map(function(layer){
116 | return layer[i];
117 | })
118 | .sort(sortBy);
119 |
120 | var sum = d3.sum(values, function(layer){ return layer.y; });
121 | var y0 = 0;
122 | values.forEach(function(layer){
123 | layer.y *= normalize() ? 100 / sum : 1;
124 | layer.y0 = y0;
125 | y0 += layer.y + padding();
126 | })
127 |
128 | })
129 |
130 | var x = date && date.type() == "Date"
131 | // Date
132 | ? d3.time.scale()
133 | .domain( [ d3.min(layers, function(layer) { return d3.min(layer, function(d) { return d.x; }); }), d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.x; }); }) ])
134 | .range([0, +width()])
135 | : date && date.type() == "String"
136 | // String
137 | ? d3.scale.ordinal()
138 | .domain(layers[0].map(function(d){ return d.x; }) )
139 | .rangePoints([0, +width()],0)
140 | // Number
141 | : d3.scale.linear()
142 | .domain( [ d3.min(layers, function(layer) { return d3.min(layer, function(d) { return d.x; }); }), d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.x; }); }) ])
143 | .range([0, +width()]);
144 |
145 | var y = d3.scale.linear()
146 | .domain([0, d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); })])
147 | .range([+height()-20, 0]);
148 |
149 | // to be improved
150 | layers[0].forEach(function(d,i){
151 |
152 | var values = layers.map(function(layer){
153 | return layer[i];
154 | })
155 | .sort(sortBy);
156 |
157 | var sum = d3.sum(values, function(layer){ return layer.y; });
158 | var y0 = normalize() ? 0 : -sum/2 + y.invert( (+height()-20)/2 ) - padding()*(values.length-1)/2;
159 |
160 | values.forEach(function(layer){
161 | layer.y *= normalize() ? 100 / sum : 1;
162 | layer.y0 = y0;
163 | y0 += layer.y + padding();
164 | })
165 |
166 | })
167 |
168 | var xAxis = d3.svg.axis().scale(x).tickSize(-height()+20).orient("bottom");
169 |
170 | g.append("g")
171 | .attr("class", "x axis")
172 | .style("stroke-width", "1px")
173 | .style("font-size","10px")
174 | .style("font-family","Arial, Helvetica")
175 | .attr("transform", "translate(" + 0 + "," + (height()-20) + ")")
176 | .call(xAxis);
177 |
178 | d3.selectAll(".x.axis line, .x.axis path")
179 | .style("shape-rendering","crispEdges")
180 | .style("fill","none")
181 | .style("stroke","#ccc")
182 |
183 | colors.domain(layers, function (d){ return d[0].group; })
184 |
185 | var area = d3.svg.area()
186 | .interpolate(curves[curve()])
187 | .x(function(d) { return x(d.x); })
188 | .y0(function(d) { return y(d.y0); })
189 | .y1(function(d) { return Math.min(y(d.y0)-1, y(d.y0 + d.y)); });
190 |
191 | var line = d3.svg.line()
192 | .interpolate(curves[curve()])
193 | .x(function(d) { return x(d.x); })
194 | .y(function(d) {
195 | var y0 = y(d.y0), y1 = y(d.y0 + d.y);
196 | return y0 + (y1 - y0) * 0.5;
197 | });
198 |
199 | g.selectAll("path.layer")
200 | .data(layers)
201 | .enter().append("path")
202 | .attr("class","layer")
203 | .attr("d", area)
204 | .attr("title", function (d){ return d[0].group; })
205 | .style("fill-opacity",.9)
206 | .style("fill", function (d) { return colors()(d[0].group); });
207 |
208 | if (!showLabels()) return;
209 |
210 | g.append('defs');
211 |
212 | g.select('defs')
213 | .selectAll('path')
214 | .data(layers)
215 | .enter().append('path')
216 | .attr('id', function(d,i) { return 'path-' + i; })
217 | .attr('d', line);
218 |
219 | g.selectAll("text.label")
220 | .data(layers)
221 | .enter().append('text')
222 | .attr('dy', '0.5ex')
223 | .attr("class","label")
224 | .append('textPath')
225 | .attr('xlink:xlink:href', function(d,i) { return '#path-' + i; })
226 | .attr('startOffset', function(d) {
227 | var maxYr = 0, maxV = 0;
228 | d3.range(layers[0].length).forEach(function(i) {
229 | if (d[i].y > maxV) {
230 | maxV = d[i].y;
231 | maxYr = i;
232 | }
233 | });
234 | d.maxVal = d[maxYr].y;
235 | d.offset = Math.round(x(d[maxYr].x) / x.range()[1] * 100);
236 | return Math.min(95, Math.max(5, d.offset))+'%';
237 | })
238 | .attr('text-anchor', function(d) {
239 | return d.offset > 90 ? 'end' : d.offset < 10 ? 'start' : 'middle';
240 | })
241 | .text(function(d){ return d[0].group; })
242 | .style("font-size","11px")
243 | .style("font-family","Arial, Helvetica")
244 | .style("font-weight","normal")
245 |
246 | function sortBy(a,b){
247 | if (sort() == 'value (descending)') return a.y - b.y;
248 | if (sort() == 'value (ascending)') return b.y - a.y;
249 | if (sort() == 'group') return a.group - b.group;
250 | }
251 |
252 | function interpolate(points) {
253 | var x0 = points[0][0], y0 = points[0][1], x1, y1, x2,
254 | path = [x0, ",", y0],
255 | i = 0,
256 | n = points.length;
257 |
258 | while (++i < n) {
259 | x1 = points[i][0], y1 = points[i][1], x2 = (x0 + x1) / 2;
260 | path.push("C", x2, ",", y0, " ", x2, ",", y1, " ", x1, ",", y1);
261 | x0 = x1, y0 = y1;
262 | }
263 | return path.join("");
264 | }
265 | })
266 |
267 | })();
--------------------------------------------------------------------------------
/partials/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Insert your data
8 |
14 |
15 |
16 |
20 |
24 |
25 |
26 |
{{data.length}} records in your data have been successfully parsed!
38 |
Whoops! Please, check line {{error+1}}
39 |
Loading your data. Please wait.
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
Ouch. It seems like RAW encountered some problems while parsing your data. Please, be sure everything is ok with your data. Often this is due to some missing delimiters. If you want to use a delimiter char as string literal, please include it in double quotes. See our FAQs for more information.