├── favicon.ico ├── favicon-l.png ├── imgs ├── gantt.png ├── images.ai ├── alluvial.png ├── barChart.png ├── beeswarm.png ├── binning.png ├── boxplot.png ├── horizon.png ├── pieChart.png ├── sunburst.png ├── treemap.png ├── voronoi.png ├── bumpChart.png ├── contourplot.png ├── dendrogram.png ├── raw_header.jpg ├── scatterPlot.png ├── streamgraph.png ├── circlePacking.png ├── rawgraphslogo.png ├── simplescatteplot.png ├── smallMultiples.png ├── circularDendrogram.png ├── cover-for-socials.jpg ├── multipleConvexHull.png └── parallelCoordinates.png ├── .gitignore ├── templates ├── dimensions.html └── colors.html ├── js ├── analytics.sample.js ├── filters.js ├── app.js └── services.js ├── data ├── lineup.tsv ├── dispersions.csv ├── countriesGDP.csv ├── letters.tsv ├── cities.csv ├── animals.tsv ├── orchestra.csv └── music.csv ├── bower.json ├── charts ├── voronoi.js ├── chart.js ├── clusterDendrogram.js ├── circularDendrogram.js ├── treemap.js ├── packing.js ├── sunburst.js ├── parallelCoordinates.js ├── hexagonalBinning.js ├── smallMultiplesArea.js ├── scatterPlot.js ├── contourPlot.js ├── boxPlot.js ├── horizonChart.js ├── pieChart.js ├── convexHullMultiple.js ├── alluvial.js ├── barChart.js ├── gantt.js ├── streamgraph.js ├── bumpChart.js └── beeswarmPlot.js ├── CONTRIBUTING.md ├── README.md ├── LICENSE └── index.html /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/favicon.ico -------------------------------------------------------------------------------- /favicon-l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/favicon-l.png -------------------------------------------------------------------------------- /imgs/gantt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/gantt.png -------------------------------------------------------------------------------- /imgs/images.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/images.ai -------------------------------------------------------------------------------- /imgs/alluvial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/alluvial.png -------------------------------------------------------------------------------- /imgs/barChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/barChart.png -------------------------------------------------------------------------------- /imgs/beeswarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/beeswarm.png -------------------------------------------------------------------------------- /imgs/binning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/binning.png -------------------------------------------------------------------------------- /imgs/boxplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/boxplot.png -------------------------------------------------------------------------------- /imgs/horizon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/horizon.png -------------------------------------------------------------------------------- /imgs/pieChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/pieChart.png -------------------------------------------------------------------------------- /imgs/sunburst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/sunburst.png -------------------------------------------------------------------------------- /imgs/treemap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/treemap.png -------------------------------------------------------------------------------- /imgs/voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/voronoi.png -------------------------------------------------------------------------------- /imgs/bumpChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/bumpChart.png -------------------------------------------------------------------------------- /imgs/contourplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/contourplot.png -------------------------------------------------------------------------------- /imgs/dendrogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/dendrogram.png -------------------------------------------------------------------------------- /imgs/raw_header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/raw_header.jpg -------------------------------------------------------------------------------- /imgs/scatterPlot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/scatterPlot.png -------------------------------------------------------------------------------- /imgs/streamgraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/streamgraph.png -------------------------------------------------------------------------------- /imgs/circlePacking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/circlePacking.png -------------------------------------------------------------------------------- /imgs/rawgraphslogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/rawgraphslogo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | node_modules/ 3 | bower_components/ 4 | components/ 5 | js/analytics.js 6 | -------------------------------------------------------------------------------- /imgs/simplescatteplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/simplescatteplot.png -------------------------------------------------------------------------------- /imgs/smallMultiples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/smallMultiples.png -------------------------------------------------------------------------------- /imgs/circularDendrogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/circularDendrogram.png -------------------------------------------------------------------------------- /imgs/cover-for-socials.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/cover-for-socials.jpg -------------------------------------------------------------------------------- /imgs/multipleConvexHull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/multipleConvexHull.png -------------------------------------------------------------------------------- /imgs/parallelCoordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/densitydesign/raw/HEAD/imgs/parallelCoordinates.png -------------------------------------------------------------------------------- /templates/dimensions.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js/analytics.sample.js: -------------------------------------------------------------------------------- 1 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 2 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 3 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 4 | })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 5 | 6 | ga('create', 'your GA code', 'auto'); 7 | ga('send', 'pageview'); 8 | -------------------------------------------------------------------------------- /js/filters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Filters */ 4 | 5 | angular.module('raw.filters', []) 6 | 7 | .filter('categoryFilter', [function () { 8 | return function (charts, category) { 9 | return charts.filter(function (chart){ 10 | return !chart.category() && category == 'Others' || chart.category() == category; 11 | }); 12 | }; 13 | }]) 14 | 15 | .filter('decodeUrl', [function () { 16 | return function (url) { 17 | if (!url) return url; 18 | return decodeURIComponent(url); 19 | }; 20 | }]) 21 | -------------------------------------------------------------------------------- /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 | 'ngFileUpload' 15 | ]) 16 | 17 | .config(['$routeProvider','$locationProvider', function ($routeProvider,$locationProvider) { 18 | $routeProvider.when('/', {templateUrl: 'partials/main.html', controller: 'RawCtrl'}); 19 | $routeProvider.otherwise({redirectTo: '/'}); 20 | $locationProvider.html5Mode(true); 21 | }]); 22 | -------------------------------------------------------------------------------- /data/lineup.tsv: -------------------------------------------------------------------------------- 1 | Name Begin End Role 2 | Steve Harris 12/25/1975 1/1/2015 Bass 3 | Paul Day 12/25/1975 7/1/1976 Vocals 4 | Dennis Wilcock 7/2/1976 3/1/1978 Vocals 5 | Paul Di'Anno 11/1/1978 10/1/1981 Vocals 6 | Bruce Dickinson 10/1/1981 8/28/1993 Vocals 7 | Blaze Bayley 1/1/1994 1/2/1999 Vocals 8 | Bruce Dickinson 1/2/1999 1/1/2015 Vocals 9 | Dave Sullivan 12/25/1975 12/1/1976 Guitars 10 | Terry Rance 12/25/1975 12/1/1976 Guitars 11 | Dave Murray 12/1/1976 6/1/1977 Guitars 12 | Dave Murray 3/1/1978 1/1/2015 Guitars 13 | Bob Sawyer 1/1/1977 6/1/1977 Guitars 14 | Terry Wapram 7/1/1977 3/1/1978 Guitars 15 | Paul Cairns 12/1/1978 3/1/1979 Guitars 16 | Paul Todd 6/23/1979 6/30/1979 Guitars 17 | Tony Parsons 9/1/1979 11/15/1979 Guitars 18 | Dennis Stratton 12/20/1979 11/1/1980 Guitars 19 | Adrian Smith 11/5/1980 1/1/1990 Guitars 20 | Adrian Smith 2/1/1999 1/1/2015 Guitars 21 | Janick Gers 2/1/1990 1/1/2015 Guitars 22 | Ron Matthews 12/25/1975 6/1/1977 Drums 23 | Thunderstick 9/1/1977 11/1/1977 Drums 24 | Doug Sampson 12/1/1977 12/23/1979 Drums 25 | Clive Burr 12/26/1979 11/1/1982 Drums 26 | Nicko McBrain 12/1/1982 1/1/2015 Drums 27 | Tony Moore 9/1/1977 11/1/1977 Keys -------------------------------------------------------------------------------- /templates/colors.html: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 |
11 | 12 | 13 |
14 | 15 | 16 | 17 | 20 | 30 | 31 | 32 |
18 |

{{color.key}}

19 |
21 | 28 |
29 |
33 |
34 | -------------------------------------------------------------------------------- /data/dispersions.csv: -------------------------------------------------------------------------------- 1 | Movie,Genre,Production Budget (millions),Box Office (millions),ROI,Rating IMDB 2 | Avatar,Action,237,2784,11.7,8.0 3 | The Blind Side,Drama,29,309,10.7,7.6 4 | "The Chronicles of Narnia: The Lion, the Witch and the Wardrobe",Adventure,180,745,4.1,6.9 5 | The Dark Knight,Action,185,1005,5.4,9.0 6 | ET: The Extra-Terrestrial,Drama,11,793,75.5,7.9 7 | Finding Nemo,Adventure,94,940,10.0,8.1 8 | Ghostbusters,Comedy,144,229,1.6,7.8 9 | The Hunger Games,Thriller/Suspense,78,649,8.3,7.2 10 | Iron Man 3,Action,178,1215,6.8,7.6 11 | Jurassic Park,Action,53,1030,19.4,8.0 12 | King Kong,Adventure,207,551,2.7,7.3 13 | The Lion King,Adventure,45,968,21.5,8.4 14 | "Monsters, Inc.",Adventure,115,577,5.0,8.0 15 | The Twilight Saga: New Moon,Drama,50,710,14.2,4.5 16 | Oz the Great and Powerful,Adventure,160,493,3.1,6.6 17 | Pirates of the Caribbean: Dead Man's Chest,Adventure,225,1066,4.7,7.3 18 | Quantum of Solace,Action,200,586,2.9,6.7 19 | Raiders of the Lost Ark,Adventure,18,390,21.7,8.7 20 | Star Wars Ep. I: The Phantom Menace,Adventure,115,1027,8.9,6.5 21 | Titanic,Thriller/Suspense,200,2187,10.9,7.6 22 | Up,Adventure,175,735,4.2,8.3 23 | The Vow,Drama,30,196,6.5,6.7 24 | The War of the Worlds,Action,132,704,5.3,6.5 25 | X-Men: The Last Stand,Action,210,459,2.2,6.8 26 | You've Got Mail,Drama,65,251,3.9,6.3 27 | Zookeeper,Romantic Comedy,80,170,2.1,5.0 -------------------------------------------------------------------------------- /data/countriesGDP.csv: -------------------------------------------------------------------------------- 1 | Country,Agricolture,Industry,Services 2 | United States,209027.00,3327015.00,13882883.00 3 | China,944615.00,4422042.00,5013724.00 4 | Japan,55396.00,1269492.00,3296063.00 5 | Germany,30876.00,1084533.00,2744138.00 6 | United Kingdom,20616.00,618481.00,2306049.00 7 | France,54091.00,520981.00,2271817.00 8 | Brazil,127063.00,644729.00,1581233.00 9 | Italy,42959.00,519804.00,1585189.00 10 | India,356319.00,528335.00,1165204.00 11 | Russia,72441.00,668686.00,1116334.00 12 | Canada,32197.00,511573.00,1244947.00 13 | Australia,57768.00,384154.00,1002267.00 14 | South Korea,38258.00,563946.00,814746.00 15 | Spain,46426.00,340459.00,1021377.00 16 | Mexico,47461.00,438692.00,796572.00 17 | Indonesia,127077.00,416776.00,344795.00 18 | Netherlands,24258.00,208791.00,634171.00 19 | Turkey,71744.00,226516.00,507848.00 20 | Saudi Arabia,15049.00,503395.00,234015.00 21 | Switzerland,9257.00,197238.00,505556.00 22 | Nigeria,102110.00,147429.00,313214.00 23 | Sweden,10064.00,150401.00,398648.00 24 | Poland,18776.00,185549.00,347905.00 25 | Belgium,3695.00,114007.00,410108.00 26 | Norway,13813.00,195944.00,301845.00 27 | Taiwan,6571.00,161745.00,338147.00 28 | Argentina,44764.00,137427.00,265005.00 29 | Austria,6541.00,128640.00,300888.00 30 | United Arab Emirates,2915.00,247368.00,165745.00 31 | Iran,45102.00,163496.00,194101.00 32 | Colombia,35610.00,152044.00,212462.00 33 | Thailand,50605.00,129367.00,200519.00 34 | Denmark,15624.00,66314.00,265258.00 35 | South Africa,8530.00,107824.00,224861.00 36 | Greece,8131.00,44105.00,194407.00 37 | Venezuela,9834.00,73020.00,126373 -------------------------------------------------------------------------------- /data/letters.tsv: -------------------------------------------------------------------------------- 1 | Letter Language Frequency Rank 2 | a English 0.08 3 3 | b English 0.01 other 4 | c English 0.03 other 5 | d English 0.04 other 6 | e English 0.13 1 7 | f English 0.02 other 8 | g English 0.02 other 9 | h English 0.06 other 10 | i English 0.07 other 11 | j English 0.00 other 12 | k English 0.01 other 13 | l English 0.04 other 14 | m English 0.02 other 15 | n English 0.07 other 16 | o English 0.08 other 17 | p English 0.02 other 18 | q English 0.00 other 19 | r English 0.06 other 20 | s English 0.06 other 21 | t English 0.09 2 22 | u English 0.03 other 23 | v English 0.01 other 24 | w English 0.02 other 25 | x English 0.00 other 26 | y English 0.02 other 27 | z English 0.00 other 28 | a German 0.07 other 29 | b German 0.02 other 30 | c German 0.03 other 31 | d German 0.05 other 32 | e German 0.16 1 33 | f German 0.02 other 34 | g German 0.03 other 35 | h German 0.05 other 36 | i German 0.07 other 37 | j German 0.00 other 38 | k German 0.01 other 39 | l German 0.03 other 40 | m German 0.03 other 41 | n German 0.10 2 42 | o German 0.03 other 43 | p German 0.01 other 44 | q German 0.00 other 45 | r German 0.07 other 46 | s German 0.07 3 47 | t German 0.06 other 48 | u German 0.04 other 49 | v German 0.01 other 50 | w German 0.02 other 51 | x German 0.00 other 52 | y German 0.00 other 53 | z German 0.01 other 54 | a Italian 0.12 2 55 | b Italian 0.01 other 56 | c Italian 0.05 other 57 | d Italian 0.04 other 58 | e Italian 0.12 1 59 | f Italian 0.01 other 60 | g Italian 0.02 other 61 | h Italian 0.01 other 62 | i Italian 0.10 3 63 | j Italian 0.00 other 64 | k Italian 0.00 other 65 | l Italian 0.07 other 66 | m Italian 0.03 other 67 | n Italian 0.07 other 68 | o Italian 0.10 other 69 | p Italian 0.03 other 70 | q Italian 0.01 other 71 | r Italian 0.06 other 72 | s Italian 0.05 other 73 | t Italian 0.06 other 74 | u Italian 0.03 other 75 | v Italian 0.02 other 76 | w Italian 0.00 other 77 | x Italian 0.00 other 78 | y Italian 0.00 other 79 | z Italian 0.01 other -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Raw", 3 | "version": "1.3.0", 4 | "authors": [ 5 | "DensityDesign Lab", 6 | "Calibro" 7 | ], 8 | "description": "The missing link between spreadsheet and data visualization", 9 | "main": "index.html", 10 | "keywords": [ 11 | "Raw" 12 | ], 13 | "license": "Apache License 2.0", 14 | "homepage": "rawgraphs.io", 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "bower_components", 19 | "test", 20 | "tests" 21 | ], 22 | "dependencies": { 23 | "angular": "1.6.10", 24 | "d3": "5.4.0", 25 | "d3-voronoi": "https://github.com/d3/d3-voronoi/releases/download/v1.1.2/d3-voronoi.zip", 26 | "d3-delaunay": "https://github.com/d3/d3-delaunay/releases/download/v2.0.1/d3-delaunay.zip", 27 | "d3-hexbin": "https://github.com/d3/d3-hexbin/releases/download/v0.2.2/d3-hexbin.zip", 28 | "d3-sankey": "https://github.com/d3/d3-sankey/releases/download/v0.7.1/d3-sankey.zip", 29 | "d3-contour": "https://github.com/d3/d3-contour/releases/download/v1.2.0/d3-contour.zip", 30 | "d3-box": "https://gist.githubusercontent.com/mikima/28d130bd380fd9be113c7040315ec036/raw/ffc793f4843311dc6a898715eec2c4492c86aeb3/box.js", 31 | "bootstrap": "3.3.6", 32 | "angular-route": "1.6.10", 33 | "angular-strap": "2.3.12", 34 | "angular-animate": "1.6.10", 35 | "jquery-ui": "1.12.1", 36 | "angular-sanitize": "1.6.10", 37 | "angular-bootstrap-colorpicker": "3.0.32", 38 | "bootstrap-colorpicker": "2.5.0", 39 | "FileSaver": "https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js", 40 | "angular-ui": "0.4.0", 41 | "zeroclipboard": "2.3.0", 42 | "canvas-toBlob.js": "https://github.com/eligrey/canvas-toBlob.js.git", 43 | "jqueryui-touch-punch": "*", 44 | "codemirror": "latest", 45 | "ng-file-upload": "12.2.13", 46 | "is_js": "0.9.0", 47 | "js-xlsx": "0.13.0" 48 | }, 49 | "resolutions": { 50 | "angular": "1.6.10" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /data/cities.csv: -------------------------------------------------------------------------------- 1 | Continent,Country,City,Population 2 | Asia,China,Shanghai,24256800 3 | Asia,Pakistan,Karachi,23500000 4 | Asia,China,Beijing,21516000 5 | Asia,India,Delhi,16787941 6 | Asia,China,Tianjin,15200000 7 | Asia,Japan,Tokyo,13513734 8 | Asia,China,Guangzhou,13080500 9 | Asia,India,Mumbai,12442373 10 | Asia,China,Shenzhen,10467400 11 | Asia,Indonesia,Jakarta,10075310 12 | Africa,Nigeria,Lagos,17578000 13 | Africa,Egypt,Cairo,11001000 14 | Africa,Democratic Republic of the Congo,Kinshasa-Brazzaville,8754000 15 | Africa,Somalia,Mogadishu,6346000 16 | Africa,Sudan,Khartoum-Omdurman,5172000 17 | Africa,Angola,Luanda,4772000 18 | Africa,Egypt,Alexandria,4387000 19 | Africa,Tanzania,Dar es Salaam,4364541 20 | Africa,Ivory Coast,Abidjan,4125000 21 | Africa,South Africa,Greater Johannesburg,3670000 22 | Europe,Turkey,Istanbul,14025646 23 | Europe,Russia,Moscow,12330126 24 | Europe,United Kingdom,London,8673713 25 | Europe,Russia,Saint Petersburg,5225690 26 | Europe,Germany,Berlin,3562166 27 | Europe,Spain,Madrid,3165235 28 | Europe,Ukraine,Kiev,2909491 29 | Europe,Italy,Rome,2874038 30 | Europe,France,Paris,2241346 31 | Europe,Belarus,Minsk,1949400 32 | North America,Mexico,Mexico City,8918653 33 | North America,United States,New York City,8550405 34 | North America,United States,Los Angeles,3971883 35 | North America,Canada,Toronto,2826498 36 | North America,United States,Chicago,2720546 37 | North America,United States,Houston,2296224 38 | North America,Cuba,Havana,2117625 39 | North America,Canada,Montreal,1753034 40 | North America,Mexico,Ecatepec de Morelos,1677678 41 | North America,United States,Philadelphia,1567442 42 | South America,Brazil,São Paulo,11967825 43 | South America,Peru,Lima,8894412 44 | South America,Colombia,Bogotá,7862277 45 | South America,Brazil,Rio de Janeiro,6476631 46 | South America,Chile,Santiago,5507282 47 | South America,Venezuela,Caracas,3289886 48 | South America,Argentina,Buenos Aires,3054267 49 | South America,Brazil,Salvador,2921087 50 | South America,Brazil,Brasília,2914830 51 | South America,Brazil,Fortaleza,2591188 -------------------------------------------------------------------------------- /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('Dispersion') 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.scaleLinear().range([0, +width()]).domain(d3.extent(data, function(d) { return d.x; })), 35 | y = d3.scaleLinear().range([+height(), 0]).domain(d3.extent(data, function(d) { return d.y; })); 36 | 37 | var voronoi = d3.voronoi() 38 | .x(function(d) { return x(d.x); }) 39 | .y(function(d) { return y(d.y); }) 40 | .extent([ 41 | [0, 0], 42 | [+width(), +height()] 43 | ]); 44 | 45 | var g = selection 46 | .attr("width", +width()) 47 | .attr("height", +height()) 48 | .append("g"); 49 | 50 | colors.domain(data, function(d) { return d.color; }); 51 | 52 | var path = g.selectAll("path") 53 | .data(voronoi.polygons(data)) 54 | .enter().append("path") 55 | .style("fill", function(d) { return d && colors() ? colors()(d.data.color) : "#dddddd"; }) 56 | .style("stroke", "#fff") 57 | .attr("d", polygon); 58 | 59 | path.order(); 60 | 61 | g.selectAll("circle") 62 | .data(data.filter(function() { return showPoints() })) 63 | .enter().append("circle") 64 | .style("fill", "#000000") 65 | .style("pointer-events", "none") 66 | .attr("transform", function(d) { return "translate(" + x(d.x) + ", " + y(d.y) + ")"; }) 67 | .attr("r", 1.5); 68 | 69 | function polygon(d) { 70 | if (!d) return; 71 | return "M" + d.join("L") + "Z"; 72 | } 73 | 74 | }) 75 | })(); -------------------------------------------------------------------------------- /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/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('Hierarchy') 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 | function linkDiagonal(d) { 27 | return "M" + d.y + "," + d.x + 28 | "C" + (d.parent.y + 100) + "," + d.x + 29 | " " + (d.parent.y + 100) + "," + d.parent.x + 30 | " " + d.parent.y + "," + d.parent.x; 31 | } 32 | 33 | chart.draw((selection, data) => { 34 | 35 | var g = selection 36 | .attr("width", +width()) 37 | .attr("height", +height()) 38 | .append("g") 39 | .attr("transform", "translate(40,0)"); 40 | 41 | var cluster = d3.cluster() 42 | .size([+height(), +width() - 160]); 43 | 44 | root = d3.hierarchy(data); 45 | 46 | cluster(root); 47 | 48 | var link = g.selectAll(".link") 49 | .data(root.descendants().slice(1)) 50 | .enter().append("path") 51 | .attr("class", "link") 52 | .style("fill", "none") 53 | .style("stroke", "#cccccc") 54 | .style("stroke-width", "1px") 55 | .attr("d", linkDiagonal); 56 | 57 | var node = g.selectAll(".node") 58 | .data(root.descendants()) 59 | .enter().append("g") 60 | .attr("class", "node") 61 | .attr("transform", d => { 62 | return `translate(${d.y}, ${d.x})`; 63 | }); 64 | 65 | node.append("circle") 66 | .attr("r", 4.5) 67 | .style("fill", "#eeeeee") 68 | .style("stroke", "#999999") 69 | .style("stroke-width", "1px"); 70 | 71 | node.append("text") 72 | .style("font-size", "11px") 73 | .style("font-family", "Arial, Helvetica") 74 | .attr("dx", d => { 75 | return d.children ? -8 : 8; 76 | }) 77 | .attr("dy", 3) 78 | .style("text-anchor", d => { 79 | return d.children ? "end" : "start"; 80 | }) 81 | .text(d => { 82 | return d.data.name; 83 | }); 84 | 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('Hierarchy') 15 | .model(tree); 16 | 17 | var diameter = chart.number() 18 | .title("Radius") 19 | .defaultValue(1000) 20 | .fitToWidth(true); 21 | 22 | function linkDiagonal(d) { 23 | return "M" + project(d.x, d.y) + 24 | "C" + project(d.x, (d.y + d.parent.y) / 2) + 25 | " " + project(d.parent.x, (d.y + d.parent.y) / 2) + 26 | " " + project(d.parent.x, d.parent.y); 27 | } 28 | 29 | function project(x, y) { 30 | var angle = (x - 90) / 180 * Math.PI, 31 | radius = y; 32 | return [radius * Math.cos(angle), radius * Math.sin(angle)]; 33 | } 34 | 35 | chart.draw((selection, data) => { 36 | var g = selection 37 | .attr("width", +diameter()) 38 | .attr("height", +diameter()) 39 | .append("g") 40 | .attr("transform", `translate(${diameter()/2}, ${diameter()/2})`); 41 | 42 | var cluster = d3.cluster() 43 | .size([360, diameter() / 2 - 120]); 44 | 45 | root = d3.hierarchy(data); 46 | 47 | cluster(root); 48 | 49 | var link = g.selectAll("path.link") 50 | .data(root.descendants().slice(1)) 51 | .enter().append("path") 52 | .attr("class", "link") 53 | .style("fill", "none") 54 | .style("stroke", "#cccccc") 55 | .style("stroke-width", "1px") 56 | .attr("d", linkDiagonal); 57 | 58 | var node = g.selectAll("g.node") 59 | .data(root.descendants()) 60 | .enter().append("g") 61 | .attr("class", "node") 62 | .attr("transform", d => { 63 | return `rotate(${d.x - 90})translate(${d.y})`; 64 | }); 65 | 66 | node.append("circle") 67 | .attr("r", 4.5) 68 | .style("fill", "#eeeeee") 69 | .style("stroke", "#999999") 70 | .style("stroke-width", "1px"); 71 | 72 | node.append("text") 73 | .attr("dy", ".31em") 74 | .attr("text-anchor", d => { 75 | return d.x < 180 ? "start" : "end"; 76 | }) 77 | .attr("transform", d => { 78 | return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; 79 | }) 80 | .text(d => { 81 | return d.data.name; 82 | }) 83 | .style("font-size", "11px") 84 | .style("font-family", "Arial, Helvetica"); 85 | 86 | }) 87 | })(); 88 | -------------------------------------------------------------------------------- /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('Hierarchy (weighted)') 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 | //unknown function 32 | var format = d3.format(",d"); 33 | 34 | // get the drawing area 35 | var g = selection 36 | .attr("width", +width()) 37 | .attr("height", +height()) 38 | .append("g") 39 | .attr("transform", "translate(.5,.5)"); 40 | 41 | 42 | // create the layout 43 | var layout = d3.treemap() 44 | .tile(d3.treemapResquarify) 45 | .padding(+padding()) 46 | .size([+width(), +height()]) 47 | //.sticky(true) 48 | //.value(function(d) { return d.size; }) 49 | 50 | // create the tree 51 | var root = d3.hierarchy(data) 52 | .sum(function(d) { return +d.size; }); 53 | 54 | // inform the layout 55 | layout(root); 56 | 57 | 58 | // define color scale 59 | colors.domain(root.descendants().filter(function(d) { return !d.children }), 60 | function(d) { return d.data.color; }); 61 | 62 | // Create cells 63 | var cell = g.selectAll("g") 64 | .data(root.leaves()) 65 | .enter().append("g") 66 | .attr("class", "cell") 67 | .attr("transform", function(d) { return "translate(" + d.x0 + "," + d.y0 + ")"; }); 68 | 69 | cell.append("rect") 70 | .attr("width", function(d) { return d.x1 - d.x0; }) 71 | .attr("height", function(d) { return d.y1 - d.y0; }) 72 | .style("fill", function(d) { return colors()(d.data.color); }) 73 | //.style("fill-opacity", function(d) { return d.children ? 0 : 1; }) 74 | .style("stroke", "#fff") 75 | 76 | cell.append("title") 77 | .text(function(d) { return d.name + ": " + format(d.size); }); 78 | 79 | cell.append("text") 80 | .attr("x", function(d) { return (d.x1 - d.x0) / 2; }) 81 | .attr("y", function(d) { return (d.y1 - d.y0) / 2; }) 82 | .attr("dy", ".35em") 83 | .attr("text-anchor", "middle") 84 | .style("font-size", "11px") 85 | .style("font-family", "Arial, Helvetica") 86 | .text(function(d) { return d.data.label ? d.data.label.join(", ") : d.data.name; }); 87 | 88 | }) 89 | })(); -------------------------------------------------------------------------------- /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('Hierarchy (weighted)') 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.scaleLinear() 42 | .range([0, innerDiameter]); 43 | 44 | var y = d3.scaleLinear() 45 | .range([0, innerDiameter]); 46 | 47 | var pack = d3.pack() 48 | .padding(+padding()) 49 | .size([innerDiameter, innerDiameter]); 50 | 51 | //compute the hierarchy 52 | var hierarchy = d3.hierarchy(data).sum(function(d) { return +d.size; }); 53 | var nodes = hierarchy 54 | .sort(function(a, b) { return sort() ? b.value - a.value : null; }) 55 | .descendants(); 56 | pack(hierarchy); 57 | 58 | var g = selection 59 | .attr("width", outerDiameter) 60 | .attr("height", outerDiameter) 61 | .append("g") 62 | .attr("transform", "translate(" + margin + "," + margin + ")"); 63 | 64 | colors.domain(nodes.filter(function(d) { return !d.children }), 65 | function(d) { return d.data.color; }); 66 | 67 | g.append("g").selectAll("circle") 68 | .data(nodes) 69 | .enter().append("circle") 70 | .attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; }) 71 | .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) 72 | .attr("r", function(d) { return d.r; }) 73 | .style("fill", function(d) { return !d.children ? colors()(d.data.color) : ''; }) 74 | .style("fill-opacity", function(d) { return !d.children ? 1 : 0; }) 75 | .style("stroke", '#ddd') 76 | .style("stroke-opacity", function(d) { return !d.children ? 0 : 1 }) 77 | 78 | g.append("g").selectAll("text") 79 | .data(nodes.filter(function(d) { return showLabels(); })) 80 | .enter().append("text") 81 | .attr("text-anchor", "middle") 82 | .style("font-size", "11px") 83 | .style("font-family", "Arial, Helvetica") 84 | .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) 85 | .text(function(d) { return d.data.label ? d.data.label.join(", ") : d.data.name; }); 86 | 87 | }) 88 | })(); -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Want to contribute to RAWGraphs'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. 4 | Then you can use Github's issues and pull requests to discuss and share your work. 5 | 6 | You will need to sign a [Contributor License Agreement (CLA)](https://www.clahub.com/agreements/densitydesign/raw) before making a submission. We adopted CLA to be sure that the project will remain open source. 7 | For more information, write us: . 8 | 9 | ## The "obvius fix" RULE 10 | 11 | Inspired by [CARTO](https://carto.com/contributions/#obvious-fix) "obvious fix" rule we decided to integrate that rule also into our contribution policy. 12 | RAWGraphs's contribution policy is aimed at encouraging broad participation from our community and minimizing risks to the project owners and our community due to inappropriate contributions of the intellectual property of others. 13 | As a general standard, RAWGraphs requires every contributor to fill out a Contributor License Agreement (“CLA”), either individually or on behalf of a corporate entity. 14 | HOWEVER, very small contributions (such as fixing spelling errors), where the content change is small enough to not be considered intellectual property, can be submitted by a contributor as a patch, without a CLA. 15 | 16 | ## How does the obvious fix rule work? 17 | 18 | Any committer may commit fixes without first signing a CLA for obvious typos, grammar mistakes, and formatting problems wherever they may be. 19 | Whenever you invoke the Obvious Fix Rule, please say so in your commit message. For example: 20 | 21 | ``` 22 | commit fed5f73b831906878a32bddaee98dcc5652f1716 23 | Author: giovanna 24 | Date: Mon Feb 06 09:41:00 2017 +0100 25 | Fix typo in README. 26 | Obvious fix. 27 | ``` 28 | 29 | ## What qualifies as an obvious fix? 30 | 31 | An obvious fix is a pull request that does not contain creative work. We rely on your judgment to determine what is “obvious”; if you’re not sure, just ask by sending an email to: hello@rawgraphs.io 32 | As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality or creative thinking. As long as the change does not affect functionality, some likely examples include the following: 33 | * Spelling/grammar fixes; 34 | * Correcting typos; 35 | * Cleaning up comments in the code; 36 | * Changes to white space or formatting; 37 | * Bug fixes that change default return values or error codes stored in constants, literals, or simple variable types; 38 | * Adding logging messages or debugging output; 39 | * Changes to ‘metadata’ files like Gemfile, rebar.config, Makefile, app.config, sys.config, .gitignore, example configuration files, build scripts, etc.; 40 | * Changes that reflect outside facts, like renaming a build directory or changing a constant; 41 | * Changes in build or installation scripts; 42 | * Re-ordering of objects or subroutines within a source file (such as alphabetizing routines); 43 | * Moving source files from one directory or package to another, with no changes in code; 44 | * Breaking a source file into multiple source files, or consolidating multiple source files into one source file, with no change in code behavior; 45 | * Changes to words or phrases isolated from their context; 46 | * Changes to typeface. 47 | -------------------------------------------------------------------------------- /charts/sunburst.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var tree = raw.models.tree(); 4 | 5 | var chart = raw.chart() 6 | .title('Sunburst') 7 | .description("A sunburst is similar to the treemap, except it uses a radial layout. The root node of the tree is at the center, with leaves on the circumference. The area (or angle, depending on implementation) of each arc corresponds to its value.
Based on http://bl.ocks.org/mbostock/4063423") 8 | .thumbnail("imgs/sunburst.png") 9 | .category('Hierarchy (weighted)') 10 | .model(tree); 11 | 12 | var diameter = chart.number() 13 | .title('Diameter') 14 | .defaultValue(600) 15 | .fitToWidth(true); 16 | 17 | var colors = chart.color() 18 | .title("Color scale"); 19 | 20 | chart.draw((selection, data) => { 21 | 22 | var radius = +diameter() / 2; 23 | 24 | var root = d3.hierarchy(data); 25 | root.sum(d => { 26 | return d.size; 27 | }); 28 | 29 | var layout = d3.partition(); 30 | 31 | var x = d3.scaleLinear() 32 | .range([0, 2 * Math.PI]); 33 | 34 | var y = d3.scaleSqrt() 35 | .range([0, radius]); 36 | 37 | var arc = d3.arc() 38 | .startAngle(d => { 39 | return Math.max(0, Math.min(2 * Math.PI, x(d.x0))); 40 | }) 41 | .endAngle(d => { 42 | return Math.max(0, Math.min(2 * Math.PI, x(d.x1))); 43 | }) 44 | .innerRadius(d => { 45 | return Math.max(0, y(d.y0)); 46 | }) 47 | .outerRadius(d => { 48 | return Math.max(0, y(d.y1)); 49 | }); 50 | 51 | var format = d3.format(",d"); 52 | 53 | var g = selection 54 | .attr("width", +diameter()) 55 | .attr("height", +diameter()) 56 | .append("g") 57 | .attr("transform", `translate(${(+diameter() / 2)}, ${(+diameter() / 2)})`); 58 | 59 | var nodes = layout(root).descendants(); 60 | 61 | colors.domain(nodes, d => { 62 | return seek(d); 63 | }); 64 | 65 | var slicesGroups = g.selectAll("g") 66 | .data(nodes) 67 | .enter().append("g") 68 | .attr("display", d => { 69 | return d.depth ? null : "none"; 70 | }); // hide inner ring 71 | 72 | slicesGroups.append("path") 73 | .attr("d", arc) 74 | .style("stroke", "#fff") 75 | .style("fill", d => { 76 | return colors()(seek(d)); 77 | }) 78 | .style("fill-rule", "evenodd"); 79 | 80 | slicesGroups.append("text") 81 | .attr("transform", d => { 82 | var ang = ((x((d.x0 + d.x1) / 2) - Math.PI / 2) / Math.PI * 180); 83 | d.textAngle = (ang > 90) ? 180 + ang : ang; 84 | return 'translate(' + arc.centroid(d) + ')rotate(' + d.textAngle + ')'; 85 | }) 86 | .attr('text-anchor', 'middle') 87 | .attr("dx", "6") 88 | .attr("dy", ".35em") 89 | .style("font-size", "11px") 90 | .style("font-family", "Arial, Helvetica") 91 | .text(d => { 92 | return d.data.label ? d.data.label.join(", ") : d.data.name; 93 | }); 94 | 95 | slicesGroups.append("title") 96 | .text(d => { 97 | var size = d.data.size ? format(d.data.size) : "none"; 98 | return d.data.name + ": " + size; 99 | }); 100 | 101 | function seek(d) { 102 | if (d.children) return seek(d.children[0]); 103 | else return d.data.color; 104 | } 105 | 106 | }) 107 | })(); 108 | -------------------------------------------------------------------------------- /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(data => { 15 | if(!list()) return; 16 | return data.map(d => { 17 | var obj = { 18 | dimensions: {}, 19 | color: color(d) 20 | }; 21 | list().forEach(l => { 22 | obj.dimensions[l] = d[l]; 23 | }) 24 | return obj; 25 | }) 26 | }); 27 | 28 | var chart = raw.chart() 29 | .title('Parallel Coordinates') 30 | .description( 31 | "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") 32 | .thumbnail("imgs/parallelCoordinates.png") 33 | .category('Multivariate') 34 | .model(model); 35 | 36 | var width = chart.number() 37 | .title("Width") 38 | .defaultValue(1000) 39 | .fitToWidth(true); 40 | 41 | var height = chart.number() 42 | .title("Height") 43 | .defaultValue(500); 44 | 45 | var colors = chart.color() 46 | .title("Color scale"); 47 | 48 | chart.draw((selection, data) => { 49 | 50 | var m = [30, 40, 10, 40], 51 | w = +width() - m[1] - m[3], 52 | h = +height() - m[0] - m[2]; 53 | 54 | var x = d3.scalePoint().range([0, w]), 55 | y = {}, 56 | dragging = {}; 57 | 58 | var line = d3.line(), 59 | background, 60 | foreground; 61 | 62 | var svg = selection 63 | .attr("width", +width()) 64 | .attr("height", +height()) 65 | .append("g") 66 | .attr("width", w + m[1] + m[3]) 67 | .attr("height", h + m[0] + m[2]) 68 | .style("font-size", "10px") 69 | .style("font-family", "Arial, Helvetica") 70 | .append("g") 71 | .attr("transform", `translate(${m[3]}, ${m[0]})`); 72 | 73 | x.domain(dimensions = d3.keys(data[0].dimensions).filter(d => { 74 | return d != "name" && (y[d] = d3.scaleLinear() 75 | .domain(d3.extent(data, p => { 76 | return +p.dimensions[d]; 77 | })) 78 | .range([h, 0])); 79 | })); 80 | 81 | colors.domain(data, d => { 82 | return d.color; 83 | }); 84 | 85 | background = svg.append("g") 86 | .attr("class", "background") 87 | .selectAll("path") 88 | .data(data) 89 | .enter().append("path") 90 | .style('fill', 'none') 91 | .style('stroke', d => { 92 | return colors()(d.color); 93 | }) 94 | .style('stroke-opacity', '.4') 95 | .attr("d", path); 96 | 97 | var g = svg.selectAll(".dim") 98 | .data(dimensions) 99 | .enter().append("g") 100 | .attr("class", "dim") 101 | .attr("transform", d => { 102 | return `translate(${x(d)})`; 103 | }); 104 | 105 | g.append("g") 106 | .attr("class", "axis") 107 | .each(function(d) { 108 | d3.select(this).call(d3.axisLeft(y[d])); 109 | }) 110 | .append("text") 111 | .attr("text-anchor", "middle") 112 | .style("font-size", "10px") 113 | .style("font-family", "Arial, Helvetica") 114 | .attr("y", -9) 115 | .text(String); 116 | 117 | d3.selectAll('text') 118 | .style("font-size", "10px") 119 | .style("font-family", "Arial, Helvetica"); 120 | 121 | d3.selectAll(".axis line, .axis path") 122 | .style('fill', 'none') 123 | .style('stroke', '#000') 124 | .style('stroke-width', '1px') 125 | .style('shape-rendering', 'crispEdges'); 126 | 127 | function position(d) { 128 | var v = dragging[d]; 129 | return v == null ? x(d) : v; 130 | } 131 | 132 | function path(d) { 133 | return line(dimensions.map(p => { 134 | return [position(p), y[p](d.dimensions[p])]; 135 | })); 136 | } 137 | 138 | }); 139 | 140 | })(); 141 | -------------------------------------------------------------------------------- /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 | flatJSON : function(array){ 12 | 13 | return array.map(function(d){ 14 | return parse_object(d); 15 | }); 16 | 17 | function parse_object(obj, path) { 18 | if (path == undefined) 19 | path = ""; 20 | 21 | var type = $.type(obj); 22 | var scalar = (type == "number" || type == "string" || type == "boolean" || type == "null"); 23 | 24 | if (type == "array" || type == "object") { 25 | var d = {}; 26 | for (var i in obj) { 27 | var newD = parse_object(obj[i], path + i + "."); 28 | $.extend(d, newD); 29 | } 30 | 31 | return d; 32 | } 33 | 34 | else if (scalar) { 35 | var d = {}; 36 | var endPath = path.substr(0, path.length-1); 37 | d[endPath] = obj; 38 | return d; 39 | } 40 | 41 | // ? 42 | else return {}; 43 | } 44 | 45 | }, 46 | 47 | loadURL : function(url) { 48 | 49 | 50 | 51 | }, 52 | 53 | loadExcel : function(file){ 54 | 55 | var deferred = $q.defer(); 56 | 57 | var reader = new FileReader(); 58 | 59 | reader.onload = function(e) { 60 | 61 | var worksheets = []; 62 | var data = e.target.result; 63 | var workbook = XLSX.read(data, {type: 'binary'}); 64 | var sheet_name_list = workbook.SheetNames; 65 | 66 | sheet_name_list.forEach(function(y) { /* iterate through sheets */ 67 | var worksheet = workbook.Sheets[y]; 68 | worksheets.push({ 69 | name: y, 70 | text : XLSX.utils.sheet_to_csv(worksheet) 71 | // rows: worksheet['!range'].e.r 72 | }) 73 | }); 74 | deferred.resolve(worksheets); 75 | }; 76 | 77 | reader.readAsBinaryString(file); 78 | 79 | return deferred.promise; 80 | 81 | }, 82 | 83 | loadJson : function(file){ 84 | var deferred = $q.defer(); 85 | 86 | var reader = new FileReader(); 87 | 88 | reader.onload = function(e) { 89 | var text = reader.result; 90 | var json = JSON.parse(text); 91 | deferred.resolve(json); 92 | }; 93 | 94 | reader.readAsText(file); 95 | 96 | return deferred.promise; 97 | }, 98 | 99 | // text file 100 | loadText : function(file){ 101 | 102 | var deferred = $q.defer(); 103 | 104 | var reader = new FileReader(); 105 | 106 | reader.onload = function(e) { 107 | var text = reader.result; 108 | deferred.resolve(text); 109 | }; 110 | 111 | reader.readAsText(file); 112 | 113 | return deferred.promise; 114 | }, 115 | 116 | loadSample : function(sample){ 117 | var deferred = $q.defer(); 118 | $http.get(sample) 119 | .then(function(response){ 120 | deferred.resolve(response.data); 121 | }, 122 | function(){ 123 | deferred.reject("An error occured while getting sample (" + sample.title + ")"); 124 | }); 125 | 126 | return deferred.promise; 127 | }, 128 | 129 | debounce : function (func, wait, immediate) { 130 | var timeout; 131 | var deferred = $q.defer(); 132 | return function() { 133 | var context = this, args = arguments; 134 | var later = function() { 135 | timeout = null; 136 | if(!immediate) { 137 | deferred.resolve(func.apply(context, args)); 138 | deferred = $q.defer(); 139 | } 140 | }; 141 | var callNow = immediate && !timeout; 142 | if ( timeout ) { 143 | $timeout.cancel(timeout); 144 | } 145 | timeout = $timeout(later, wait); 146 | if (callNow) { 147 | deferred.resolve(func.apply(context,args)); 148 | deferred = $q.defer(); 149 | } 150 | return deferred.promise; 151 | }; 152 | } 153 | 154 | } 155 | }) 156 | -------------------------------------------------------------------------------- /data/animals.tsv: -------------------------------------------------------------------------------- 1 | Name Kingdom Phylum Class Order Family 2 | Spotted Hyena Animalia Chordata Mammalia Carnivora Hyaenidae 3 | Woolly mammoth Animalia Chordata Mammalia Proboscidea Elephantidae 4 | Nine-banded armadillo Animalia Chordata Mammalia Cingulata Dasypodidae 5 | Donkey Animalia Chordata Mammalia Perissodactyla Equidae 6 | American bison Animalia Chordata Mammalia Artiodactyla Bovidae 7 | Brown-throated sloth Animalia Chordata Mammalia Pilosa Bradypodidae 8 | Dog Animalia Chordata Mammalia Carnivora Canidae 9 | Eastern grey kangaroo Animalia Chordata Mammalia Diprotodontia Macropodidae 10 | Goat Animalia Chordata Mammalia Artiodactyla Bovidae 11 | Little Owl Animalia Chordata Aves Strigiformes Strigidae 12 | Arctic hare Animalia Chordata Mammalia Lagomorpha Leporidae 13 | European rabbit Animalia Chordata Mammalia Lagomorpha Leporidae 14 | Roborovski hamster Animalia Chordata Mammalia Rodentia Cricetidae 15 | Common bottlenose dolphin Animalia Chordata Mammalia Cetacea Delphinidae 16 | Dodo Animalia Chordata Aves Columbiformes Columbidae 17 | Dugong Animalia Chordata Mammalia Sirenia Dugongidae 18 | Harbor seal Animalia Chordata Mammalia Carnivora Phocidae 19 | Weaver ant Animalia Arthropoda Insecta Hymenoptera Formicidae 20 | Giraffe Animalia Chordata Mammalia Artiodactyla Giraffidae 21 | Koala Animalia Chordata Mammalia Diprotodontia Phascolarctidae 22 | Llama Animalia Chordata Mammalia Artiodactyla Camelidae 23 | Lion Animalia Chordata Mammalia Carnivora Felidae 24 | Narwhal Animalia Chordata Mammalia Cetacea Monodontidae 25 | Platypus Animalia Chordata Mammalia Monotremata Ornithorhynchidae 26 | Brown Bear Animalia Chordata Mammalia Carnivora Ursidae 27 | Hairy Hermit Crab Animalia Arthropoda Malacostraca Decapoda Paguridae 28 | Giant Panda Animalia Chordata Mammalia Carnivora Ursidae 29 | Black Rat Animalia Chordata Mammalia Rodentia Muridae 30 | Alligator Animalia Chordata Reptilia Crocodylia Alligatoridae 31 | Alligator snapping turtle Animalia Chordata Reptilia Testudines Chelydridae 32 | House Sparrow Animalia Chordata Aves Passeriformes Passeridae 33 | Mediterranean mussel Animalia Mollusca Bivalvia Mytiloida Mytilidae 34 | Pacific Gull Animalia Chordata Aves Charadriiformes Laridae 35 | Common Magpie Animalia Chordata Aves Passeriformes Corvidae 36 | Black Woodpecker Animalia Chordata Aves Piciformes Picidae 37 | Great White Pelican Animalia Chordata Aves Pelecaniformes Pelecanidae 38 | Boa constrictor Animalia Chordata Reptilia Squamata Boidae 39 | Chans megastick Animalia Arthropoda Insecta Phasmatodea Phasmatidae 40 | Golden Stag Beetle Animalia Arthropoda Insecta Coleoptera Lucanidae 41 | russet mite Animalia Arthropoda Arachnida Prostigmata Eriophyidae 42 | Firefly Animalia Arthropoda Insecta Coleoptera Lampyridae 43 | Ringlet Butterfly Animalia Arthropoda Insecta Lepidoptera Nymphalidae 44 | European wasp Animalia Arthropoda Insecta Hymenoptera Vespidae 45 | Ostrich Animalia Chordata Aves Struthioniformes Struthionidae 46 | Tarantula Animalia Arthropoda Arachnida Araneae Lycosidae 47 | giant forest scorpions Animalia Arthropoda Chelicerata Scorpiones Scorpionidae 48 | Pterodactylus Animalia Chordata Reptilia †Pterosauria †Pterodactylidae 49 | Zebra Animalia Chordata Mammalia Perissodactyla Equidae 50 | African civet Animalia Chordata Mammalia Carnivora Viverridae 51 | Mink Animalia Chordata Mammalia Carnivora Mustelidae 52 | Cynoscion nebulosus Animalia Chordata Actinopterygii Perciformes Sciaenidae 53 | Walrus Animalia Chordata Mammalia Carnivora Odobenidae 54 | Tiger Animalia Chordata Mammalia Carnivora Felidae 55 | Tortoise Animalia Chordata Reptilia Chelonii Testudinidae 56 | Scorpionfish Animalia Chordata Actinopterygii Scorpaeniformes Scorpaenidae 57 | Hedgehog Animalia Chordata Mammalia Erinaceomorpha Erinaceidae 58 | Reindeer Animalia Chordata Mammalia Artiodactyla Cervidae 59 | Lophius piscatorius Animalia Chordata Actinopterygii Lophiiformes Lophiidae 60 | Guinea pig Animalia Chordata Mammalia Rodentia Caviidae 61 | Penguin Animalia Chordata Aves Sphenisciformes Spheniscidae 62 | Blowfish Animalia Chordata Actinopterygii Tetraodontiformes Tetraodontidae 63 | Cockatoo Animalia Chordata Aves Psittaciformes Cacatuidae 64 | Leopard Animalia Chordata Mammalia Carnivora Felidae 65 | Killer whale Animalia Chordata Mammalia Cetacea Delphinidae 66 | Bowhead Whale Animalia Chordata Mammalia Cetacea Balaenidae -------------------------------------------------------------------------------- /data/orchestra.csv: -------------------------------------------------------------------------------- 1 | Orchestra type,Group,Instrument,Number 2 | Modern orchestra,Brass,Baritone horn,1 3 | Early Romantic orchestra,Woodwinds,Bass Clarinet,1 4 | Late Romantic orchestra,Woodwinds,Bass Clarinet,1 5 | Early Romantic orchestra,Percussion,Bass Drum,1 6 | Late Romantic orchestra,Percussion,Bass Drum,1 7 | Modern orchestra,Percussion,Bass Drum,1 8 | Classical orchestra,Woodwinds,Bassoons,2 9 | Early Romantic orchestra,Woodwinds,Bassoons,2 10 | Late Romantic orchestra,Woodwinds,Bassoons,3 11 | Modern orchestra,Woodwinds,Bassoons,4 12 | Late Romantic orchestra,Keyboards,Celesta,1 13 | Modern orchestra,Keyboards,Celesta,1 14 | Classical orchestra,Strings,Cellos,2 15 | Early Romantic orchestra,Strings,Cellos,8 16 | Late Romantic orchestra,Strings,Cellos,10 17 | Modern orchestra,Strings,Cellos,12 18 | Late Romantic orchestra,Percussion,Chimes,1 19 | Classical orchestra,Woodwinds,Clarinets,2 20 | Modern orchestra,Woodwinds,Clarinets,4 21 | Early Romantic orchestra,Woodwinds,Clarinets,2 22 | Late Romantic orchestra,Woodwinds,Clarinets,3 23 | Early Romantic orchestra,Woodwinds,Contrabassoon,1 24 | Late Romantic orchestra,Woodwinds,Contrabassoon,1 25 | Early Romantic orchestra,Brass,Cornet,2 26 | Early Romantic orchestra,Percussion,Cymbals,1 27 | Late Romantic orchestra,Percussion,Cymbals,1 28 | Modern orchestra,Percussion,Cymbals,1 29 | Classical orchestra,Strings,Double basses,1 30 | Early Romantic orchestra,Strings,Double basses,6 31 | Late Romantic orchestra,Strings,Double basses,8 32 | Modern orchestra,Strings,Double basses,10 33 | Early Romantic orchestra,Woodwinds,English Horn,1 34 | Late Romantic orchestra,Woodwinds,English Horn,1 35 | Classical orchestra,Woodwinds,Flutes,2 36 | Early Romantic orchestra,Woodwinds,Flutes,2 37 | Late Romantic orchestra,Woodwinds,Flutes,3 38 | Modern orchestra,Woodwinds,Flutes,4 39 | Classical orchestra,Brass,French Horns,4 40 | Early Romantic orchestra,Brass,French Horns,4 41 | Late Romantic orchestra,Brass,French Horns,8 42 | Modern orchestra,Brass,French Horns,8 43 | Early Romantic orchestra,Percussion,Glockenspiel,1 44 | Late Romantic orchestra,Percussion,Glockenspiel,1 45 | Modern orchestra,Percussion,Glockenspiel,1 46 | Early Romantic orchestra,Strings,Harps,1 47 | Late Romantic orchestra,Strings,Harps,2 48 | Modern orchestra,Strings,Harps,2 49 | Modern orchestra,Percussion,Marimba,1 50 | Classical orchestra,Woodwinds,Oboes,2 51 | Early Romantic orchestra,Woodwinds,Oboes,2 52 | Late Romantic orchestra,Woodwinds,Oboes,3 53 | Modern orchestra,Woodwinds,Oboes,4 54 | Late Romantic orchestra,Keyboards,Piano,1 55 | Modern orchestra,Keyboards,Piano,1 56 | Early Romantic orchestra,Woodwinds,Piccolo,1 57 | Late Romantic orchestra,Woodwinds,Piccolo,1 58 | Modern orchestra,Keyboards,Pipe organ,1 59 | Early Romantic orchestra,Percussion,Snare Drum,1 60 | Late Romantic orchestra,Percussion,Snare Drum,1 61 | Modern orchestra,Percussion,Snare Drum,1 62 | Late Romantic orchestra,Percussion,Tam-tam,1 63 | Modern orchestra,Percussion,Tam-tam,1 64 | Early Romantic orchestra,Percussion,Tambourine,1 65 | Late Romantic orchestra,Percussion,Tambourine,1 66 | Modern orchestra,Percussion,Tambourine,1 67 | Modern orchestra,Percussion,Tenor drum,1 68 | Classical orchestra,Percussion,Timpani,2 69 | Early Romantic orchestra,Percussion,Timpani,3 70 | Late Romantic orchestra,Percussion,Timpani,4 71 | Modern orchestra,Percussion,Timpani,1 72 | Early Romantic orchestra,Percussion,Triangle,1 73 | Late Romantic orchestra,Percussion,Triangle,1 74 | Modern orchestra,Percussion,Triangle,1 75 | Early Romantic orchestra,Brass,Trombones,3 76 | Late Romantic orchestra,Brass,Trombones,4 77 | Modern orchestra,Brass,Trombones,6 78 | Classical orchestra,Brass,Trumpets,2 79 | Early Romantic orchestra,Brass,Trumpets,2 80 | Late Romantic orchestra,Brass,Trumpets,4 81 | Modern orchestra,Brass,Trumpets,6 82 | Early Romantic orchestra,Brass,Tubas,1 83 | Late Romantic orchestra,Brass,Tubas,2 84 | Modern orchestra,Brass,Tubas,2 85 | Modern orchestra,Percussion,Tubular bells,1 86 | Modern orchestra,Percussion,Vibraphone,1 87 | Classical orchestra,Strings,Violas,4 88 | Early Romantic orchestra,Strings,Violas,10 89 | Late Romantic orchestra,Strings,Violas,12 90 | Modern orchestra,Strings,Violas,14 91 | Classical orchestra,Strings,Violins 1,6 92 | Early Romantic orchestra,Strings,Violins 1,14 93 | Late Romantic orchestra,Strings,Violins 1,16 94 | Modern orchestra,Strings,Violins 1,18 95 | Classical orchestra,Strings,Violins 2,6 96 | Early Romantic orchestra,Strings,Violins 2,12 97 | Late Romantic orchestra,Strings,Violins 2,14 98 | Modern orchestra,Strings,Violins 2,16 99 | Late Romantic orchestra,Brass,Wagner Tuba,1 100 | Modern orchestra,Percussion,Wood block,1 101 | Late Romantic orchestra,Percussion,Xylophone,1 102 | Modern orchestra,Percussion,Xylophone,1 103 | -------------------------------------------------------------------------------- /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('Dispersion') 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 | //left margin 27 | var marginLeft = chart.number() 28 | .title('Left Margin') 29 | .defaultValue(40) 30 | 31 | var radius = chart.number() 32 | .title("Radius") 33 | .defaultValue(20) 34 | 35 | var useZero = chart.checkbox() 36 | .title("Set origin at (0,0)") 37 | .defaultValue(false) 38 | 39 | var colors = chart.color() 40 | .title("Color scale") 41 | 42 | var showPoints = chart.checkbox() 43 | .title("Show points") 44 | .defaultValue(true) 45 | 46 | chart.draw(function(selection, data) { 47 | 48 | // Retrieving dimensions from model 49 | var x = points.dimensions().get('x'), 50 | y = points.dimensions().get('y'); 51 | 52 | var g = selection 53 | .attr("width", +width()) 54 | .attr("height", +height()) 55 | .append("g") 56 | 57 | //define margins 58 | var margin = { 59 | top: 0, 60 | right: 0, 61 | bottom: 20, 62 | left: marginLeft() 63 | }; 64 | 65 | var w = width() - margin.left, 66 | h = height() - margin.bottom; 67 | 68 | var xExtent = !useZero() ? d3.extent(data, function(d) { 69 | return d.x; 70 | }) : [0, d3.max(data, function(d) { 71 | return d.x; 72 | })], 73 | yExtent = !useZero() ? d3.extent(data, function(d) { 74 | return d.y; 75 | }) : [0, d3.max(data, function(d) { 76 | return d.y; 77 | })]; 78 | 79 | var xScale = x.type() == "Date" ? 80 | d3.scaleTime().range([margin.left, width()]).domain(xExtent) : 81 | d3.scaleLinear().range([margin.left, width()]).domain(xExtent); 82 | 83 | var yScale = y.type() == "Date" ? 84 | d3.scaleTime().range([h, 0]).domain(yExtent) : 85 | d3.scaleLinear().range([h, 0]).domain(yExtent); 86 | 87 | var xAxis = d3.axisBottom(xScale).tickSize(6, -h); 88 | var yAxis = d3.axisLeft(yScale).ticks(10).tickSize(6, -w); 89 | 90 | var hexbin = d3.hexbin() 91 | .size([w, h]) 92 | .x(function(d) { 93 | return xScale(d.x); 94 | }) 95 | .y(function(d) { 96 | return yScale(d.y); 97 | }) 98 | .radius(+radius()); 99 | 100 | g.append("clipPath") 101 | .attr("id", "clip") 102 | .append("rect") 103 | .attr("class", "mesh") 104 | .attr("width", w) 105 | .attr("height", h) 106 | .attr("transform", "translate(" + margin.left + ",1)"); 107 | 108 | colors.domain(hexbin(data), function(d) { 109 | return d.length; 110 | }); 111 | 112 | g.append("g") 113 | .attr("clip-path", "url(#clip)") 114 | .selectAll(".hexagon") 115 | .data(hexbin(data)) 116 | .enter().append("path") 117 | .attr("class", "hexagon") 118 | .attr("d", hexbin.hexagon()) 119 | .attr("transform", function(d) { 120 | return "translate(" + d.x + "," + d.y + ")"; 121 | }) 122 | .style("fill", function(d) { 123 | return colors()(d.length); 124 | }) 125 | .attr("stroke", "#000") 126 | .attr("stroke-width", ".5px") 127 | 128 | var point = g.selectAll("g.point") 129 | .data(data) 130 | .enter().append("g") 131 | .attr("class", "point") 132 | 133 | point.append("circle") 134 | .filter(function() { 135 | return showPoints(); 136 | }) 137 | .style("fill", "#000") 138 | .attr("transform", function(d) { 139 | return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"; 140 | }) 141 | .attr("r", 1); 142 | 143 | g.append("g") 144 | .attr("class", "y axis") 145 | .attr("transform", "translate(" + margin.left + ",0)") 146 | .call(yAxis); 147 | 148 | g.append("g") 149 | .attr("class", "x axis") 150 | .attr("transform", "translate(0," + h + ")") 151 | .call(xAxis); 152 | 153 | g.selectAll(".axis") 154 | .selectAll("text") 155 | .style("font", "10px Arial, Helvetica") 156 | 157 | g.selectAll(".axis") 158 | .selectAll("path") 159 | .style("fill", "none") 160 | .style("stroke", "#000000") 161 | .style("shape-rendering", "crispEdges") 162 | 163 | g.selectAll(".axis") 164 | .selectAll("line") 165 | .style("fill", "none") 166 | .style("stroke", "#000000") 167 | .style("shape-rendering", "crispEdges") 168 | }) 169 | })(); 170 | -------------------------------------------------------------------------------- /charts/smallMultiplesArea.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var stream = raw.models.timeSeries(); 4 | 5 | var chart = raw.chart() 6 | .title('Area graph') 7 | .thumbnail("imgs/smallMultiples.png") 8 | .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") 9 | .category('Time series') 10 | .model(stream) 11 | 12 | var width = chart.number() 13 | .title("Width") 14 | .defaultValue(1000) 15 | .fitToWidth(true) 16 | 17 | var height = chart.number() 18 | .title("Height") 19 | .defaultValue(500) 20 | 21 | var padding = chart.number() 22 | .title("Padding") 23 | .defaultValue(5) 24 | 25 | var scale = chart.checkbox() 26 | .title("Use same scale") 27 | .defaultValue(false) 28 | 29 | var specular = chart.checkbox() 30 | .title("Center values vertically") 31 | .defaultValue(false) 32 | 33 | var colors = chart.color() 34 | .title("Color scale") 35 | 36 | var curve = chart.list() 37 | .title("Interpolation") 38 | .values(['Cardinal', 'Basis spline', 'DensityDesign', 'Linear']) 39 | .defaultValue('DensityDesign') 40 | 41 | var sorting = chart.list() 42 | .title("Sort by") 43 | .values(['Original', 'Total (descending)', 'Total (ascending)', 'Name']) 44 | .defaultValue('Original') 45 | 46 | // interpolation function 47 | 48 | function CurveSankey(context) { 49 | this._context = context; 50 | } 51 | 52 | CurveSankey.prototype = { 53 | areaStart: function() { 54 | this._line = 0; 55 | }, 56 | areaEnd: function() { 57 | this._line = NaN; 58 | }, 59 | lineStart: function() { 60 | this._x = this._y = NaN; 61 | this._point = 0; 62 | }, 63 | lineEnd: function() { 64 | if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); 65 | this._line = 1 - this._line; 66 | }, 67 | point: function(x, y) { 68 | x = +x, y = +y; 69 | switch (this._point) { 70 | case 0: 71 | this._point = 1; 72 | this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); 73 | break; 74 | case 1: 75 | this._point = 2; // proceed 76 | default: 77 | var mx = (x - this._x) / 2 + this._x; 78 | this._context.bezierCurveTo(mx, this._y, mx, y, x, y); 79 | break; 80 | } 81 | this._x = x, this._y = y; 82 | } 83 | }; 84 | 85 | var curveSankey = function(context) { 86 | return new CurveSankey(context); 87 | } 88 | 89 | chart.draw(function(selection, data) { 90 | 91 | //sort data 92 | function sortBy(a, b) { 93 | if (sorting() == 'Total (descending)') { 94 | return a.values.reduce(function(c, d) { return c + d.size }, 0) - b.values.reduce(function(c, d) { return c + d.size }, 0) 95 | } 96 | if (sorting() == 'Total (ascending)') { 97 | return b.values.reduce(function(c, d) { return c + d.size }, 0) - a.values.reduce(function(c, d) { return c + d.size }, 0); 98 | } 99 | if (sorting() == 'Name') { 100 | return d3.ascending(a.key, b.key); 101 | } 102 | } 103 | 104 | data.sort(sortBy); 105 | 106 | var curves = { 107 | 'Basis spline': d3.curveBasis, 108 | 'Cardinal': d3.curveCardinal, 109 | 'DensityDesign': curveSankey, 110 | 'Linear': d3.curveLinear 111 | } 112 | 113 | var w = +width(), 114 | h = (+height() - 20 - (+padding() * (data.length - 1))) / data.length; 115 | 116 | var svg = selection 117 | .attr("width", +width()) 118 | .attr("height", +height()) 119 | 120 | var x = d3.scaleTime() 121 | .range([0, w]); 122 | 123 | var y = d3.scaleLinear() 124 | .range([h, 0]); 125 | 126 | var area = d3.area() 127 | .x(function(d) { return x(d.date); }) 128 | .curve(curves[curve()]); 129 | 130 | if (specular()) { 131 | area.y0(function(d) { return h - y(d.size) / 2; }) 132 | .y1(function(d) { return y(d.size) / 2; }) 133 | } else { 134 | area.y0(h) //align to baseline 135 | .y1(function(d) { return y(d.size); }) 136 | } 137 | 138 | x.domain([ 139 | d3.min(data, function(layer) { return d3.min(layer.values, function(d) { return d.date; }); }), 140 | d3.max(data, function(layer) { return d3.max(layer.values, function(d) { return d.date; }); }) 141 | ]) 142 | 143 | colors.domain(data, function(d) { return d.values[0].color; }) //get color of first item 144 | 145 | var xAxis = d3.axisBottom(x).tickSize(-height() + 15); 146 | 147 | svg.append("g") 148 | .attr("class", "x axis") 149 | .style("stroke-width", "1px") 150 | .style("font-size", "10px") 151 | .style("font-family", "Arial, Helvetica") 152 | .attr("transform", "translate(" + 0 + "," + (height() - 15) + ")") 153 | .call(xAxis); 154 | 155 | d3.selectAll(".x.axis line, .x.axis path") 156 | .style("shape-rendering", "crispEdges") 157 | .style("fill", "none") 158 | .style("stroke", "#ccc") 159 | 160 | 161 | svg.selectAll("g.flow") 162 | .data(data) 163 | .enter().append("g") 164 | .attr("class", "flow") 165 | .attr("title", function(d) { return d.key; }) 166 | .attr("transform", function(d, i) { return "translate(0," + ((h + padding()) * i) + ")" }) 167 | .each(multiple); 168 | 169 | svg.selectAll("g.flow") 170 | .append("text") 171 | .attr("x", w - 6) 172 | .attr("y", h - 6) 173 | .style("font-size", "10px") 174 | .style("fill", "black") 175 | .style("font-family", "Arial, Helvetica") 176 | .style("text-anchor", "end") 177 | .text(function(d) { return d.key; }); 178 | 179 | function multiple(single) { 180 | 181 | var g = d3.select(this); 182 | 183 | if (scale()) { 184 | y.domain([0, d3.max(data, function(layer) { return d3.max(layer.values, function(d) { return d.size; }); })]) 185 | } else { 186 | y.domain([0, d3.max(single.values, function(d) { return d.size; })]); 187 | } 188 | 189 | g.append("path") 190 | .attr("class", "area") 191 | .style("fill", function(d) { return colors()(d.values[0].color); }) 192 | .attr("d", area(single.values)); 193 | } 194 | 195 | }) 196 | 197 | })(); 198 | -------------------------------------------------------------------------------- /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('Dispersion') 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 | //left margin 23 | var marginLeft = chart.number() 24 | .title('Left Margin') 25 | .defaultValue(40) 26 | 27 | var maxRadius = chart.number() 28 | .title("max radius") 29 | .defaultValue(20); 30 | 31 | var useZero = chart.checkbox() 32 | .title("set origin at (0,0)") 33 | .defaultValue(false); 34 | 35 | var colors = chart.color() 36 | .title("Color scale"); 37 | 38 | var showPoints = chart.checkbox() 39 | .title("show points") 40 | .defaultValue(true); 41 | 42 | chart.draw((selection, data) => { 43 | 44 | // Retrieving dimensions from model 45 | var x = points.dimensions().get('x'), 46 | y = points.dimensions().get('y'); 47 | 48 | //define margins 49 | var margin = { 50 | top: +maxRadius(), 51 | right: +maxRadius(), 52 | bottom: 20 + maxRadius(), 53 | left: marginLeft() 54 | }; 55 | 56 | var w = width() - margin.left - margin.right, 57 | h = height() - margin.bottom - margin.top; 58 | 59 | var g = selection 60 | .attr("width", +width()) 61 | .attr("height", +height()) 62 | .append("g") 63 | .attr('transform','translate(' + margin.left + ',' + margin.top + ')') 64 | 65 | var xExtent = !useZero() ? d3.extent(data, d => { 66 | return d.x; 67 | }) : [0, d3.max(data, d => { 68 | return d.x; 69 | })], 70 | yExtent = !useZero() ? d3.extent(data, d => { 71 | return d.y; 72 | }) : [0, d3.max(data, d => { 73 | return d.y; 74 | })]; 75 | 76 | var xScale = x.type() == "Date" ? 77 | d3.scaleTime().range([0, w]).domain(xExtent) : 78 | d3.scaleLinear().range([0, w]).domain(xExtent), 79 | yScale = y.type() == "Date" ? 80 | d3.scaleTime().range([h, 0]).domain(yExtent) : 81 | d3.scaleLinear().range([h, 0]).domain(yExtent), 82 | sizeScale = d3.scaleSqrt().range([1, +maxRadius()]) 83 | .domain([0, d3.max(data, d => { 84 | return d.size; 85 | })]), 86 | xAxis = d3.axisBottom(xScale).tickSize(-h) //.tickSubdivide(true), 87 | yAxis = d3.axisLeft(yScale).ticks(10).tickSize(-w); 88 | 89 | g.append("g") 90 | .attr("class", "x axis") 91 | .style("stroke-width", "1px") 92 | .style("font-size", "10px") 93 | .style("font-family", "Arial, Helvetica") 94 | //.attr("transform", `translate(0, ${h - maxRadius()})`) 95 | .attr('transform', 'translate(0,' + h + ')') 96 | .call(xAxis); 97 | 98 | g.append("g") 99 | .attr("class", "y axis") 100 | .style("stroke-width", "1px") 101 | .style("font-size", "10px") 102 | .style("font-family", "Arial, Helvetica") 103 | //.attr("transform", `translate(${margin.left}, 0)`) 104 | .call(yAxis); 105 | 106 | d3.selectAll(".y.axis line, .x.axis line, .y.axis path, .x.axis path") 107 | .style("shape-rendering", "crispEdges") 108 | .style("fill", "none") 109 | .style("stroke", "#ccc"); 110 | 111 | var circle = g.selectAll("g.circle") 112 | .data(data) 113 | .enter().append("g") 114 | .attr("class", "circle"); 115 | 116 | var point = g.selectAll("g.point") 117 | .data(data) 118 | .enter().append("g") 119 | .attr("class", "point") 120 | 121 | colors.domain(data, d => { 122 | return d.color; 123 | }); 124 | 125 | circle.append("circle") 126 | .style("fill", d => { 127 | return colors() ? colors()(d.color) : "#eeeeee"; 128 | }) 129 | .style("fill-opacity", .9) 130 | .attr("transform", d => { 131 | return `translate(${xScale(d.x)}, ${yScale(d.y)})`; 132 | }) 133 | .attr("r", d => { 134 | return sizeScale(d.size); 135 | }); 136 | 137 | point.append("circle") 138 | .filter(d => { 139 | return showPoints(); 140 | }) 141 | .style("fill", "#000") 142 | .attr("transform", d => { 143 | return `translate(${xScale(d.x)}, ${yScale(d.y)})`; 144 | }) 145 | .attr("r", 1); 146 | 147 | circle.append("text") 148 | .attr("transform", d => { 149 | return `translate(${xScale(d.x)}, ${yScale(d.y)})`; 150 | }) 151 | .attr("text-anchor", "middle") 152 | .style("font-size", "10px") 153 | .attr("dy", 15) 154 | .style("font-family", "Arial, Helvetica") 155 | .text(d => { 156 | return d.label ? d.label.join(", ") : ""; 157 | }); 158 | 159 | }) 160 | 161 | })(); 162 | -------------------------------------------------------------------------------- /charts/contourPlot.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('Contour Plot') 11 | .description( 12 | "It shows the estimated density of point clouds, which is especially useful to avoid overplotting in large datasets.
Based on Density Contours II") 13 | .thumbnail("imgs/contourplot.png") 14 | .category('Dispersion') 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 | //left margin 27 | var marginLeft = chart.number() 28 | .title('Left Margin') 29 | .defaultValue(40) 30 | 31 | var bandwidth = chart.number() 32 | .title("Standard deviation") 33 | .defaultValue(40) 34 | 35 | var colorMode = chart.list() 36 | .title("Colors applied to") 37 | .values(["Stroke", "Fill"]) 38 | .defaultValue("Stroke") 39 | 40 | var useZero = chart.checkbox() 41 | .title("Set origin at (0,0)") 42 | .defaultValue(false) 43 | 44 | var colors = chart.color() 45 | .title("Color scale") 46 | 47 | var showPoints = chart.checkbox() 48 | .title("Show points") 49 | .defaultValue(true) 50 | 51 | chart.draw(function(selection, data) { 52 | 53 | // Retrieving dimensions from model 54 | var x = points.dimensions().get('x'), 55 | y = points.dimensions().get('y'); 56 | 57 | var g = selection 58 | .attr("width", +width()) 59 | .attr("height", +height()) 60 | .append("g") 61 | 62 | //define margins 63 | var margin = { 64 | top: 0, 65 | right: 0, 66 | bottom: 20, 67 | left: marginLeft() 68 | }; 69 | 70 | var w = width() - margin.left, 71 | h = height() - margin.bottom; 72 | 73 | var xExtent = !useZero() ? d3.extent(data, function(d) { 74 | return d.x; 75 | }) : [0, d3.max(data, function(d) { 76 | return d.x; 77 | })], 78 | yExtent = !useZero() ? d3.extent(data, function(d) { 79 | return d.y; 80 | }) : [0, d3.max(data, function(d) { 81 | return d.y; 82 | })]; 83 | 84 | var xScale = x.type() == "Date" ? 85 | d3.scaleTime().range([margin.left, width()]).domain(xExtent) : 86 | d3.scaleLinear().range([margin.left, width()]).domain(xExtent); 87 | 88 | var yScale = y.type() == "Date" ? 89 | d3.scaleTime().range([h, 0]).domain(yExtent) : 90 | d3.scaleLinear().range([h, 0]).domain(yExtent); 91 | 92 | var xAxis = d3.axisBottom(xScale).tickSize(6, -h); 93 | var yAxis = d3.axisLeft(yScale).ticks(10).tickSize(6, -w); 94 | 95 | g.append("clipPath") 96 | .attr("id", "clip") 97 | .append("rect") 98 | .attr("class", "mesh") 99 | .attr("width", w) 100 | .attr("height", h) 101 | .attr("transform", "translate(" + margin.left + ",1)"); 102 | 103 | var contours = d3.contourDensity() 104 | .x(function(d) { 105 | return xScale(d.x); 106 | }) 107 | .y(function(d) { 108 | return yScale(d.y); 109 | }) 110 | .size([w, h]) 111 | .bandwidth(bandwidth()) 112 | (data); 113 | 114 | colors.domain(contours, function(d) { 115 | return d.value; 116 | }); 117 | 118 | var contourPaths = g.insert("g", "g") 119 | .attr("clip-path", "url(#clip)") 120 | .attr("stroke-linejoin", "round") 121 | .selectAll("path") 122 | .data(contours) 123 | .enter().append("path") 124 | .attr("d", d3.geoPath()); 125 | 126 | if (colorMode() == "Fill") { 127 | 128 | contourPaths.attr("fill", function(d) { 129 | return colors()(d.value) 130 | }) 131 | .attr("stroke", "none") 132 | 133 | } else if (colorMode() == "Stroke") { 134 | 135 | contourPaths.attr("stroke", function(d) { 136 | return colors()(d.value) 137 | }) 138 | .attr("fill", "none") 139 | } 140 | 141 | var point = g.selectAll("g.point") 142 | .data(data) 143 | .enter().append("g") 144 | .attr("class", "point") 145 | 146 | point.append("circle") 147 | .filter(function() { 148 | return showPoints(); 149 | }) 150 | .style("fill", "#000") 151 | .attr("transform", function(d) { 152 | return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")"; 153 | }) 154 | .attr("r", 1); 155 | 156 | var labels = g.selectAll("g.labels") 157 | .data(data) 158 | .enter().append("g") 159 | .attr("class", "labels") 160 | 161 | labels.append("text") 162 | .attr("transform", d => { 163 | return `translate(${xScale(d.x)}, ${yScale(d.y)})`; 164 | }) 165 | .attr("text-anchor", "middle") 166 | .style("font-size", "10px") 167 | .attr("dy", 15) 168 | .style("font-family", "Arial, Helvetica") 169 | .text(d => { 170 | return d.label ? d.label.join(", ") : ""; 171 | }); 172 | 173 | g.append("g") 174 | .attr("class", "y axis") 175 | .attr("transform", "translate(" + margin.left + ",0)") 176 | .call(yAxis); 177 | 178 | g.append("g") 179 | .attr("class", "x axis") 180 | .attr("transform", "translate(0," + h + ")") 181 | .call(xAxis); 182 | 183 | g.selectAll(".axis") 184 | .selectAll("text") 185 | .style("font", "10px Arial, Helvetica") 186 | 187 | g.selectAll(".axis") 188 | .selectAll("path") 189 | .style("fill", "none") 190 | .style("stroke", "#000000") 191 | .style("shape-rendering", "crispEdges") 192 | 193 | g.selectAll(".axis") 194 | .selectAll("line") 195 | .style("fill", "none") 196 | .style("stroke", "#000000") 197 | .style("shape-rendering", "crispEdges") 198 | }) 199 | })(); 200 | -------------------------------------------------------------------------------- /charts/boxPlot.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var model = raw.model(); 4 | 5 | var group = model.dimension() 6 | .title('Group') 7 | .types(String) 8 | 9 | var values = model.dimension() 10 | .title('Size') 11 | .types(Number) 12 | .required(1) 13 | 14 | var colorsDimension = model.dimension() 15 | .title('Colors') 16 | .types(Number, String) 17 | 18 | model.map(function(data) { 19 | 20 | var remap = data.map(function(d){ 21 | return { 22 | group: group(d), 23 | value: +values(d), 24 | color: colorsDimension(d) 25 | } 26 | }) 27 | 28 | var nest = d3.nest() 29 | .key(function(d) { 30 | return d.group 31 | }) 32 | .entries(remap) 33 | return nest 34 | 35 | }) 36 | 37 | var chart = raw.chart() 38 | .title("Box plot") 39 | .description("A box-and-whisker plot uses simple glyphs that summarize a quantitative distribution with five standard statistics: the smallest value, lower quartile, median, upper quartile, and largest value.
Based on https://bl.ocks.org/mbostock/4061502") 40 | .thumbnail("imgs/boxplot.png") 41 | .category('Distribution') 42 | .model(model) 43 | 44 | var width = chart.number() 45 | .title('Width') 46 | .defaultValue(900) 47 | 48 | var height = chart.number() 49 | .title('Height') 50 | .defaultValue(600) 51 | 52 | var marginLeft = chart.number() 53 | .title('Left margin') 54 | .defaultValue(60) 55 | 56 | var barWidth = chart.number() 57 | .title('Bars width') 58 | .defaultValue(20) 59 | 60 | var iqrValue = chart.number() 61 | .title('Interquartile range (IQR)') 62 | .defaultValue(1.5) 63 | 64 | var colors = chart.color() 65 | .title("Color scale") 66 | 67 | 68 | chart.draw(function(selection, data) { 69 | 70 | 71 | var chartMargin = { 72 | top: 20, 73 | right: 20, 74 | bottom: 20, 75 | left: marginLeft() 76 | }, 77 | chartWidth = width() - chartMargin.left - chartMargin.right, 78 | chartHeight = height() - chartMargin.top - chartMargin.bottom; 79 | 80 | var boxplot = d3.box() 81 | .whiskers(iqr(iqrValue())) 82 | .height(chartHeight); 83 | 84 | var container = selection 85 | .attr("width", chartWidth + chartMargin.left + chartMargin.right) 86 | .attr("height", chartHeight + chartMargin.top + chartMargin.bottom) 87 | .append("g") 88 | .attr("transform", "translate(" + chartMargin.left + "," + chartMargin.top + ")") 89 | 90 | var max = d3.max(data, function(d) { 91 | return d3.max(d.values, function(e) { 92 | return e.value 93 | }); 94 | }); 95 | 96 | var min = d3.min(data, function(d) { 97 | return d3.min(d.values, function(e) { 98 | return e.value 99 | }); 100 | }); 101 | 102 | boxplot.domain([min, max]); 103 | 104 | var x = d3.scaleBand() 105 | .domain(data.map(function(d) { 106 | return d.key 107 | })) 108 | .rangeRound([0, chartWidth]); 109 | 110 | var xAxis = d3.axisBottom(x) 111 | 112 | var y = d3.scaleLinear() 113 | .domain([min, max]) 114 | .range([chartHeight, 0]); 115 | 116 | var yAxis = d3.axisLeft(y) 117 | 118 | // draw axis 119 | container.append("g") 120 | .attr("class", "x axis") 121 | .style("stroke-width", "1px") 122 | .style("font-size", "10px") 123 | .style("font-family", "Arial, Helvetica") 124 | .attr("transform", "translate(0," + chartHeight + ")") 125 | .call(xAxis) 126 | 127 | container.append("g") 128 | .attr("class", "y axis") 129 | .style("stroke-width", "1px") 130 | .style("font-size", "10px") 131 | .style("font-family", "Arial, Helvetica") 132 | .call(yAxis) 133 | 134 | var boxdata = data.map(function(d) { 135 | var output = d.values.map(function(e) { 136 | return e.value 137 | }) 138 | return { 139 | key: d.key, 140 | color: d.values[0].color, 141 | values: output 142 | } 143 | }) 144 | 145 | //define colors 146 | colors.domain(boxdata, function(d){ 147 | return d.color; 148 | }); 149 | 150 | var gplot = container.selectAll(".box") 151 | .data(boxdata) 152 | 153 | var xoffset = x.bandwidth()/2 - barWidth()/2; 154 | 155 | gplot 156 | .enter().append("g") 157 | .attr("class", "box") 158 | .attr("transform", function(d) { 159 | return "translate(" + (xoffset + x(d.key)) + ",0)"; 160 | }) 161 | .style("font-size", "10px") 162 | .style("font-family", "Arial, Helvetica") 163 | .style("fill", function(d){ return colors() ? colors()(d.color) : "#eee";}) 164 | .call(function(d) { 165 | var data = d.data(); 166 | data = data.map(function(e) { 167 | return e.values 168 | }) 169 | d.data(data); 170 | boxplot.width(barWidth())(d); 171 | }); 172 | 173 | //styling 174 | 175 | 176 | d3.selectAll('.box line, .box rect') 177 | .style("stroke", "#000") 178 | 179 | d3.selectAll('.box circle') 180 | .style("stroke", "#ccc") 181 | 182 | d3.selectAll('.box text') 183 | .style("fill", "#000") 184 | 185 | d3.selectAll('.box .center') 186 | .style('stroke-dasharray', '3,3') 187 | 188 | d3.selectAll('.box .outlier') 189 | .style('fill', 'none') 190 | 191 | d3.selectAll(".y.axis line, .x.axis line, .y.axis path, .x.axis path") 192 | .style("shape-rendering", "crispEdges") 193 | .style("fill", "none") 194 | .style("stroke", "#ccc") 195 | 196 | function iqr(k) { 197 | return function(d, i) { 198 | var q1 = d.quartiles[0], 199 | q3 = d.quartiles[2], 200 | iqr = (q3 - q1) * k, 201 | i = -1, 202 | j = d.length; 203 | while (d[++i] < q1 - iqr); 204 | while (d[--j] > q3 + iqr); 205 | return [i, j]; 206 | }; 207 | } 208 | 209 | }) 210 | })(); 211 | -------------------------------------------------------------------------------- /charts/horizonChart.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var stream = raw.models.timeSeries(); 4 | stream.dimensions().remove('color'); 5 | 6 | var chart = raw.chart() 7 | .title('Horizon graph') 8 | .thumbnail("imgs/horizon.png") 9 | .description("Horizon charts combine position and color to reduce vertical space.

Based on http://bl.ocks.org/mbostock/1483226") 10 | .category('Time series') 11 | .model(stream) 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 padding = chart.number() 23 | .title("Padding") 24 | .defaultValue(5) 25 | 26 | var scale = chart.checkbox() 27 | .title("Use same scale") 28 | .defaultValue(false) 29 | 30 | var bands = chart.number() 31 | .title('Bands') 32 | .defaultValue(4) 33 | 34 | var curve = chart.list() 35 | .title("Interpolation") 36 | .values(['Cardinal', 'Basis spline', 'Sankey', 'Linear']) 37 | .defaultValue('Sankey') 38 | 39 | var sorting = chart.list() 40 | .title("Sort by") 41 | .values(['Original', 'Total (descending)', 'Total (ascending)', 'Name']) 42 | .defaultValue('Original') 43 | 44 | var colors = chart.color() 45 | .title("Color scale") 46 | 47 | // interpolation function 48 | 49 | function CurveSankey(context) { 50 | this._context = context; 51 | } 52 | 53 | CurveSankey.prototype = { 54 | areaStart: function() { 55 | this._line = 0; 56 | }, 57 | areaEnd: function() { 58 | this._line = NaN; 59 | }, 60 | lineStart: function() { 61 | this._x = this._y = NaN; 62 | this._point = 0; 63 | }, 64 | lineEnd: function() { 65 | if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); 66 | this._line = 1 - this._line; 67 | }, 68 | point: function(x, y) { 69 | x = +x, y = +y; 70 | switch (this._point) { 71 | case 0: 72 | this._point = 1; 73 | this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); 74 | break; 75 | case 1: 76 | this._point = 2; // proceed 77 | default: 78 | var mx = (x - this._x) / 2 + this._x; 79 | this._context.bezierCurveTo(mx, this._y, mx, y, x, y); 80 | break; 81 | } 82 | this._x = x, this._y = y; 83 | } 84 | }; 85 | 86 | var curveSankey = function(context) { 87 | return new CurveSankey(context); 88 | } 89 | 90 | chart.draw(function(selection, data) { 91 | 92 | //define colors 93 | colors.domain(['Negative', 'Positive']); 94 | 95 | //sort data 96 | function sortBy(a, b) { 97 | if (sorting() == 'Total (descending)') { 98 | return a.values.reduce(function(c, d) { return c + d.size }, 0) - b.values.reduce(function(c, d) { return c + d.size }, 0) 99 | } 100 | if (sorting() == 'Total (ascending)') return b.values.reduce(function(c, d) { return c + d.size }, 0) - a.values.reduce(function(c, d) { return c + d.size }, 0); 101 | if (sorting() == 'Name') { 102 | return d3.ascending(a.key, b.key); 103 | } 104 | } 105 | 106 | data.sort(sortBy); 107 | 108 | //add unique id, needed for clipping paths 109 | data.forEach(function(d, i) { 110 | d['id'] = i; 111 | }) 112 | 113 | var curves = { 114 | 'Basis spline': d3.curveBasis, 115 | 'Cardinal': d3.curveCardinal, 116 | 'Sankey': curveSankey, 117 | 'Linear': d3.curveLinear 118 | } 119 | 120 | var w = +width(), 121 | h = (+height() - 20 - (+padding() * (data.length - 1))) / data.length; 122 | 123 | var svg = selection 124 | .attr("width", +width()) 125 | .attr("height", +height()) 126 | 127 | var x = d3.scaleTime() 128 | .range([0, w]); 129 | 130 | var y = d3.scaleLinear() 131 | .range([h * bands(), 0]); 132 | 133 | var area = d3.area() 134 | .x(function(d) { return x(d.date); }) 135 | .y0(h) //align to baseline 136 | .y1(function(d) { return y(d.size); }) 137 | .curve(curves[curve()]) 138 | 139 | x.domain([ 140 | d3.min(data, function(layer) { return d3.min(layer.values, function(d) { return d.date; }); }), 141 | d3.max(data, function(layer) { return d3.max(layer.values, function(d) { return d.date; }); }) 142 | ]) 143 | 144 | var xAxis = d3.axisBottom(x).tickSize(-height() + 20); 145 | 146 | svg.append("g") 147 | .attr("class", "x axis") 148 | .style("stroke-width", "1px") 149 | .style("font-size", "10px") 150 | .style("font-family", "Arial, Helvetica") 151 | .attr("transform", "translate(" + 0 + "," + (height() - 20) + ")") 152 | .call(xAxis); 153 | 154 | d3.selectAll(".x.axis line, .x.axis path") 155 | .style("shape-rendering", "crispEdges") 156 | .style("fill", "none") 157 | .style("stroke", "#ccc") 158 | 159 | 160 | svg.selectAll("g.flow") 161 | .data(data) 162 | .enter().append("g") 163 | .attr("class", "flow") 164 | .attr("title", function(d) { return d.key; }) 165 | .attr("transform", function(d, i) { return "translate(0," + ((h + padding()) * i) + ")" }) 166 | .each(multiple); 167 | 168 | svg.selectAll("g.flow") 169 | .append("text") 170 | .attr("x", w - 6) 171 | .attr("y", h - 6) 172 | .style("font-size", "10px") 173 | .style("fill", "black") 174 | .style("font-family", "Arial, Helvetica") 175 | .style("text-anchor", "end") 176 | .text(function(d) { return d.key; }); 177 | 178 | function multiple(single) { 179 | 180 | var g = d3.select(this); 181 | 182 | var maxValue; 183 | 184 | if (scale()) { 185 | maxValue = d3.max(data, function(layer) { return d3.max(layer.values, function(d) { return Math.abs(d.size); }); }); 186 | } else { 187 | maxValue = d3.max(single.values, function(d) { return Math.abs(d.size); }); 188 | } 189 | 190 | var maxPos = d3.max(single.values, function(d) { return d.size; }) 191 | 192 | // define new range and domain according to the masimum value 193 | // and the amount of bands 194 | y.domain([0, maxValue]) 195 | .range([h, -h * (bands() - 1)]); 196 | 197 | //create the clip path 198 | g.append("clipPath") 199 | .attr("id", function(d) { return "clip_" + d.id }) 200 | .append("rect") 201 | .attr("width", w) 202 | .attr("height", h) 203 | 204 | //append one area per band, positive 205 | for (var i = 0; i < bands() * maxPos / maxValue; i++) { 206 | //change y range according to offset 207 | area.y0(h + h * i) 208 | .y1(function(d) { return y(d.size) + h * i; }) 209 | 210 | g.append("path") 211 | .attr("class", "area") 212 | .style("fill", colors()('Positive')) 213 | .attr("d", area(single.values)) 214 | .attr("opacity", (i + 1) / bands()) 215 | .attr("clip-path", function(d) { return "url(#clip_" + d.id + ")" }); 216 | } 217 | 218 | //do the same for negative values 219 | var maxNeg = d3.min(single.values, function(d) { return d.size; }) 220 | 221 | for (var i = 0; i < bands() * -maxNeg / maxValue; i++) { 222 | 223 | area.y0(-h * i) 224 | .y1(function(d) { return y(d.size) + -h * (i + 1); }) 225 | 226 | g.append("path") 227 | .attr("class", "area") 228 | .style("fill", colors()('Negative')) 229 | .attr("d", area(single.values)) 230 | .attr("opacity", (i + 1) / bands()) 231 | .attr("clip-path", function(d) { return "url(#clip_" + d.id + ")" }); 232 | } 233 | 234 | } 235 | 236 | }) 237 | 238 | })(); 239 | -------------------------------------------------------------------------------- /charts/pieChart.js: -------------------------------------------------------------------------------- 1 | ! function() { 2 | 3 | var model = raw.model(); 4 | 5 | // Group dimension. It will mainly provide a label for each 6 | // chart. If multiple lines share the same value in the 7 | // group dimension, they will be grouped. 8 | var group = model.dimension() 9 | .title('Label'); 10 | 11 | // 'Dimensions' dimension. accept multiple values. 12 | // Each value represent a slice of the pie. 13 | var dimensions = model.dimension() 14 | .title('Arcs') 15 | .types(Number) 16 | .required(true) 17 | .multiple(true); 18 | 19 | // Mapping function. 20 | // For each record in the dataset a pie chart abstraction is created. 21 | // Records are grouped according the 'group' variable. 22 | 23 | model.map(function(data) { 24 | 25 | // Check if dimensions are set. 26 | // In theory should be not necessary, to be fixed. 27 | if (dimensions() != null) { 28 | 29 | var index = 0; 30 | var nest = d3.nest() 31 | // If groups are not defined, assign a number to each record. 32 | .key(function(d) { 33 | return group() ? group(d) : ++index; 34 | }) 35 | .rollup(function(d) { 36 | return dimensions().map(function(dimension) { 37 | return { 38 | key: dimension, 39 | size: d3.sum(d, function(a) { 40 | return +a[dimension]; 41 | }) 42 | } 43 | }) 44 | }) 45 | .entries(data); 46 | 47 | return nest; 48 | } 49 | 50 | }) 51 | 52 | // The chart object 53 | 54 | var chart = raw.chart() 55 | .title("Pie chart") 56 | .description("A pie chart (or a circle chart) is a circular statistical graphic which is divided into slices to illustrate numerical proportion.") 57 | .thumbnail("imgs/pieChart.png") 58 | .category('Other') 59 | .model(model); 60 | 61 | var width = chart.number() 62 | .title('Width') 63 | .defaultValue(800) 64 | 65 | var columns = chart.number() 66 | .title('Columns') 67 | .defaultValue(4) 68 | 69 | var padding = chart.number() 70 | .title('Padding') 71 | .defaultValue(10) 72 | 73 | var donut = chart.checkbox() 74 | .title('Donut chart') 75 | .defaultValue(false) 76 | 77 | var thickness = chart.number() 78 | .title('Thickness') 79 | .defaultValue(10) 80 | 81 | var showValues = chart.checkbox() 82 | .title('Show values') 83 | .defaultValue(false) 84 | 85 | var sortChartsBy = chart.list() 86 | .title("Sort charts by") 87 | .values(['size', 'name']) 88 | .defaultValue('size') 89 | 90 | var sortArcsBy = chart.list() 91 | .title("Sort arcs by") 92 | .values(['size', 'name']) 93 | .defaultValue('size') 94 | 95 | // Chart colors 96 | var colors = chart.color() 97 | .title("Color scale") 98 | 99 | // Drawing function. 100 | 101 | chart.draw(function(selection, data) { 102 | 103 | var radius = +width() / +columns() / 2 - +padding() / 2; 104 | 105 | // Define color scale domain 106 | // Get the list of all possible values from first element 107 | // Use it to define the colors domain 108 | var allColors = data[0].value.map(function(item) { 109 | return item.key 110 | }); 111 | colors.domain(allColors); 112 | 113 | var h = Math.ceil(data.length / +columns()) * (radius * 2 + padding()); 114 | 115 | selection 116 | .attr("width", +width()) 117 | .attr("height", h) 118 | 119 | var area = d3.scaleLinear() 120 | .domain([0, d3.max(data, function(layer) { 121 | return d3.sum(layer.value, function(d) { 122 | return d.size; 123 | }); 124 | })]) 125 | .range([0, radius * radius * Math.PI]) 126 | 127 | // Sort pie slices according to the 'sortArcsBy' function 128 | var pie = d3.pie() 129 | .sort(sortArcsByComparator) 130 | .value(function(d) { 131 | return d.size; 132 | }); 133 | 134 | // Sort data according to the 'sortChartsBy' function 135 | data.sort(sortChartsByComparator); 136 | 137 | data.forEach(function(l, li) { 138 | 139 | var outerRadius = Math.sqrt(area(d3.sum(l.value, function(d) { 140 | return d.size; 141 | })) / Math.PI); 142 | 143 | var arc = d3.arc() 144 | .outerRadius(outerRadius) 145 | .innerRadius(donut() && thickness() < outerRadius ? outerRadius - +thickness() : 0) 146 | 147 | var g = selection 148 | .append("g") 149 | .attr("transform", function() { 150 | return "translate(" + (li % +columns() * (+radius * 2 + +padding()) + +radius + padding() / 2) + "," + (Math.floor(li / +columns()) * (radius * 2 + +padding()) + radius + padding() / 2) + ")"; 151 | }) 152 | 153 | var p = g.selectAll(".arc") 154 | .data(pie(l.value)) 155 | .enter().append("g") 156 | .attr("class", "arc"); 157 | 158 | p.append("path") 159 | .attr("d", arc) 160 | .style("fill", function(d) { 161 | return colors()(d.data.key); 162 | }); 163 | 164 | if (showValues() == true) { 165 | 166 | var labels = g.selectAll(".numbers") 167 | .data(pie(l.value)) 168 | .enter().append("text") 169 | .attr("transform", function(d) { 170 | return "translate(" + arc.centroid(d) + ")"; 171 | }) 172 | .attr("dy", ".35em") 173 | .style("text-anchor", "middle") 174 | .style("font-size", "10px") 175 | .style("font-family", "Arial, Helvetica") 176 | .style("fill", "#424242") 177 | .text(function(d) { 178 | return d.data.size.toLocaleString() 179 | }); 180 | } 181 | 182 | var label = g.append("text") 183 | .attr("y", outerRadius + 10) 184 | .text(l.key) 185 | .style("text-anchor", "middle") 186 | .style("font-size", "11px") 187 | .style("font-family", "Arial, Helvetica") 188 | 189 | }) 190 | 191 | 192 | function sortChartsByComparator(a, b) { 193 | if (sortChartsBy() == 'size') { 194 | return d3.descending(d3.sum(a.value, function(d) { 195 | return d.size; 196 | }), 197 | d3.sum(b.value, function(d) { 198 | return d.size; 199 | })); 200 | } 201 | if (sortChartsBy() == 'name') return d3.ascending(a.key, b.key); 202 | 203 | } 204 | 205 | function sortArcsByComparator(a, b) { 206 | if (sortArcsBy() == 'size') return d3.descending(a.size, b.size); 207 | if (sortArcsBy() == 'name') return d3.ascending(a.key, b.key); 208 | } 209 | 210 | 211 | }) 212 | 213 | 214 | 215 | }() 216 | -------------------------------------------------------------------------------- /charts/convexHullMultiple.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var model = raw.model(); 4 | 5 | var mx = model.dimension() 6 | .title("X Axis") 7 | .types(Number, Date) 8 | .accessor(function(d) { 9 | return this.type() == "Date" ? new Date(d) : +d; 10 | }) 11 | .required(1); 12 | 13 | var my = model.dimension() 14 | .title("Y Axis") 15 | .types(Number, Date) 16 | .accessor(function(d) { 17 | return this.type() == "Date" ? new Date(d) : +d; 18 | }) 19 | .required(1); 20 | 21 | var mlabel = model.dimension() 22 | .title('label') 23 | 24 | var mgroup = model.dimension().title('Group') 25 | 26 | model.map(data => { 27 | 28 | var nest = d3.nest() 29 | .key(mgroup) 30 | .rollup(g => { 31 | return g.map(d => { 32 | return { 33 | group: mgroup(d), 34 | y: my(d), 35 | x: mx(d), 36 | label: mlabel(d) 37 | }; 38 | }) 39 | }) 40 | .entries(data) 41 | 42 | return nest; 43 | 44 | }) 45 | 46 | var chart = raw.chart() 47 | .title('Convex Hull') 48 | .description( 49 | "In mathematics, the convex hull is the smallest convex shape containing a set of points. Applied to a scatterplot, it is useful to identify points belonging to the same category.

Based on http://bl.ocks.org/mbostock/4341699") 50 | .thumbnail("imgs/multipleConvexHull.png") 51 | .model(model) 52 | .category('Dispersion') 53 | 54 | 55 | var width = chart.number() 56 | .title("Width") 57 | .defaultValue(800) 58 | .fitToWidth(true); 59 | 60 | var height = chart.number() 61 | .title("Height") 62 | .defaultValue(600); 63 | 64 | //left margin 65 | var marginLeft = chart.number() 66 | .title('Left Margin') 67 | .defaultValue(40) 68 | 69 | var dotRadius = chart.number() 70 | .title("Dots Diameter") 71 | .defaultValue(6); 72 | 73 | var useZero = chart.checkbox() 74 | .title("set origin at (0,0)") 75 | .defaultValue(false); 76 | 77 | var stroke = chart.number() 78 | .title("Stroke Width") 79 | .defaultValue(32); 80 | 81 | var colors = chart.color() 82 | .title("Color scale"); 83 | 84 | chart.draw((selection, data) => { 85 | 86 | var xmin = d3.min(data, layer => { 87 | return d3.min(layer.value, d => { 88 | return d.x; 89 | }); 90 | }); 91 | var xmax = d3.max(data, layer => { 92 | return d3.max(layer.value, d => { 93 | return d.x; 94 | }); 95 | }); 96 | var ymin = d3.min(data, layer => { 97 | return d3.min(layer.value, d => { 98 | return d.y; 99 | }); 100 | }); 101 | var ymax = d3.max(data, layer => { 102 | return d3.max(layer.value, d => { 103 | return d.y; 104 | }); 105 | }) 106 | 107 | //define margins 108 | var margin = { 109 | top: 0, 110 | right: 0, 111 | bottom: 15, 112 | left: marginLeft() 113 | }; 114 | 115 | var w = +width() - stroke() - margin.left; 116 | var h = +height() - stroke() - margin.bottom; 117 | 118 | var x = d3.scaleLinear().range([0, w]), 119 | y = d3.scaleLinear().range([h, 0]); 120 | 121 | 122 | //set domain according to "origin" variable 123 | 124 | if (useZero()) { 125 | x.domain([0, xmax]); 126 | y.domain([0, ymax]); 127 | } else { 128 | x.domain([xmin, xmax]); 129 | y.domain([ymin, ymax]); 130 | } 131 | 132 | //define colors 133 | colors.domain(data, layer => { 134 | return layer.key; 135 | }); 136 | 137 | //@TODO: add x and y axes (copy from scatterPlot.js) 138 | 139 | var xAxis = d3.axisBottom(x).tickSize(-h); 140 | var yAxis = d3.axisLeft(y).tickSize(-w); 141 | 142 | var svg = selection 143 | .attr("width", +width()) 144 | .attr("height", +height()) 145 | 146 | svg.append("g") 147 | .attr("class", "x axis") 148 | .style("stroke-width", "1px") 149 | .style("font-size", "10px") 150 | .style("font-family", "Arial, Helvetica") 151 | .attr("transform", `translate(${stroke() / 2 + margin.left}, ${height() - stroke() / 2 - margin.bottom})`) 152 | .call(xAxis); 153 | 154 | svg.append("g") 155 | .attr("class", "y axis") 156 | .style("stroke-width", "1px") 157 | .style("font-size", "10px") 158 | .style("font-family", "Arial, Helvetica") 159 | .attr("transform", `translate(${stroke() / 2 + margin.left}, ${stroke() / 2})`) 160 | .call(yAxis); 161 | 162 | d3.selectAll(".y.axis line, .x.axis line, .y.axis path, .x.axis path") 163 | .style("shape-rendering", "crispEdges") 164 | .style("fill", "none") 165 | .style("stroke", "#ccc") 166 | 167 | 168 | //for each group... 169 | data.forEach(layer => { 170 | 171 | var vertices = layer.value.map(d => { 172 | return [x(d.x), y(d.y)] 173 | }) 174 | 175 | //assure that there are at least 4 vertices (required by convex hull) 176 | //add 0.01 to slightly move from position (otherwise convex hull won't work) 177 | //dirty but working. 178 | while (vertices.length < 3) { 179 | vertices.push([vertices[0][0] + 0.01, vertices[0][1] + 0.01]); 180 | } 181 | 182 | var g = svg.append("g") 183 | .attr("id", layer.key) 184 | .attr("transform", `translate(${stroke() / 2 + margin.left}, ${stroke() / 2})`) 185 | 186 | var gcolor = colors()(layer.key); 187 | 188 | g.append("path") 189 | .datum(d3.polygonHull(vertices)) 190 | .style("fill", gcolor) 191 | .style("opacity", 0.3) 192 | .style("stroke", gcolor) 193 | .style("stroke-width", +stroke()) 194 | .style("stroke-linejoin", "round") 195 | .attr("d", d => { 196 | return "M" + d.join("L") + "Z"; 197 | }); 198 | 199 | g.selectAll("circle") 200 | .data(vertices) 201 | .enter().append("circle") 202 | .style("fill", gcolor) 203 | .attr("r", dotRadius() / 2) 204 | .attr("transform", d => { 205 | return `translate(${d})`; 206 | }) 207 | }) 208 | 209 | // now, add label above all 210 | if (mlabel() != null) { 211 | var txt_group = svg.append('g') 212 | .attr("transform", `translate(${stroke() / 2 + margin.left}, ${stroke() / 2})`) 213 | 214 | data.forEach(layer => { 215 | 216 | layer.value.forEach(item => { 217 | txt_group.append("text") 218 | .attr("transform", `translate(${x(item.x)}, ${y(item.y)})`) 219 | .attr("text-anchor", "middle") 220 | .style("font-size", "10px") 221 | .style("font-family", "Arial, Helvetica") 222 | .text(item.label) 223 | }) 224 | }); 225 | } 226 | }) 227 | })(); 228 | -------------------------------------------------------------------------------- /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 DensityDesign's work with Fineo, it is inspired by https://bl.ocks.org/mbostock/ca9a0bb7ba204d12974bca90acc507c0") 9 | .thumbnail("imgs/alluvial.png") 10 | .category("Multi categorical") 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 opacity = chart.number() 27 | .title("Links opacity") 28 | .defaultValue(.4); 29 | 30 | var sortBy = chart.list() 31 | .title("Sort by") 32 | .values(['size', 'name', 'automatic']) 33 | .defaultValue('size'); 34 | 35 | var colors = chart.color() 36 | .title("Color scale"); 37 | 38 | chart.draw((selection, data) => { 39 | 40 | // get the drawing area 41 | var g = selection 42 | .attr("width", +width()) 43 | .attr("height", +height() + 20) 44 | .append("g") 45 | .attr("transform", "translate(0, 10)"); 46 | 47 | // define numbers formatting 48 | var formatNumber = d3.format(",.0f"), 49 | format = function(d) { 50 | return formatNumber(d); 51 | }; 52 | 53 | // Calculating the best nodePadding (TODO: improve) 54 | var nested = d3.nest() 55 | .key(function(d) { 56 | return d.group; 57 | }) 58 | .rollup(function(d) { 59 | return d.length; 60 | }) 61 | .entries(data.nodes); 62 | 63 | var maxNodes = d3.max(nested, function(d) { 64 | return d.values; 65 | }); 66 | var bestPadding = d3.min([10, (height() - maxNodes) / maxNodes]) 67 | 68 | // create sankey object 69 | var sankey = d3.sankey() 70 | .nodeWidth(+nodeWidth()) 71 | .nodePadding(bestPadding) 72 | .size([+width(), +height()]); 73 | 74 | // use the loaded data 75 | sankey(data); 76 | 77 | // define colors 78 | colors.domain(data.links, function(d) { 79 | return d.source.group + " - " + d.source.name; 80 | }); 81 | 82 | // add values to nodes 83 | data.nodes.forEach(function(d) { 84 | // get height for each node 85 | d.dx = d.x1 - d.x0; 86 | d.dy = d.y1 - d.y0; 87 | // check if the name is a number 88 | 89 | if (!isNaN(+d.name)) { 90 | d.name = +d.name; 91 | } 92 | }) 93 | 94 | // Re-sorting nodes 95 | var nested = d3.nest() 96 | .key(function(d) { 97 | return d.group; 98 | }) 99 | .entries(data.nodes) 100 | 101 | nested 102 | .forEach(function(d) { 103 | 104 | var y = (height() - d3.sum(d.values, function(n) { 105 | return n.dy + sankey.nodePadding(); 106 | })) / 2 + sankey.nodePadding() / 2; 107 | 108 | d.values.sort(function(a, b) { 109 | if (sortBy() == "automatic") return b.y0 - a.y0; 110 | if (sortBy() == "size") return b.dy - a.dy; 111 | //if (sortBy() == "name") return a.name < b.name ? -1 : a.name > b.name ? 1 : 0; 112 | if (sortBy() == "name") { 113 | var a1 = typeof a.name, 114 | b1 = typeof b.name; 115 | return a1 < b1 ? -1 : a1 > b1 ? 1 : a.name < b.name ? -1 : a.name > b.name ? 1 : 0; 116 | } 117 | }) 118 | 119 | d.values.forEach(function(node) { 120 | node.y0 = y; 121 | y += node.dy + sankey.nodePadding(); 122 | }) 123 | }) 124 | 125 | // Resorting links 126 | 127 | nested.forEach(function(d) { 128 | 129 | d.values.forEach(function(node) { 130 | 131 | var ly = node.y0; 132 | 133 | node.sourceLinks 134 | .sort(function(a, b) { 135 | return a.target.y0 - b.target.y0; 136 | }) 137 | .forEach(function(link) { 138 | link.y0 = ly + link.width / 2; 139 | ly += link.width; 140 | }) 141 | 142 | ly = node.y0; 143 | 144 | node.targetLinks 145 | .sort(function(a, b) { 146 | return a.source.y0 - b.source.y0; 147 | }) 148 | .forEach(function(link) { 149 | link.y1 = ly + link.width / 2; 150 | ly += link.width; 151 | }) 152 | }) 153 | }) 154 | 155 | //prepare link 156 | var link = g.append("g") 157 | .attr("class", "links") 158 | .attr("fill", "none") 159 | .attr("stroke-opacity", +opacity()) 160 | .selectAll("path") 161 | .data(data.links) 162 | .enter().append("path") 163 | .attr("d", d3.sankeyLinkHorizontal()) 164 | .style("stroke", function(d) { 165 | return colors()(d.source.group + " - " + d.source.name); 166 | }) 167 | .attr("stroke-width", function(d) { 168 | return d.width; 169 | }); 170 | 171 | 172 | //prepare node 173 | var node = g.append("g") 174 | .attr("class", "nodes") 175 | .attr("font-family", "Arial, Helvetica") 176 | .attr("font-size", 10) 177 | .selectAll("g") 178 | .data(data.nodes) 179 | .enter().append("g"); 180 | 181 | //add rectangle 182 | node.append("rect") 183 | .attr("x", function(d) { 184 | return d.x0; 185 | }) 186 | .attr("y", function(d) { 187 | return d.y0; 188 | }) 189 | .attr("height", function(d) { 190 | return d.dy; 191 | }) 192 | .attr("width", function(d) { 193 | return d.dx; 194 | }) 195 | .attr("fill", function(d) { 196 | return '#000' 197 | }); 198 | 199 | 200 | //add labels 201 | node.append("text") 202 | .attr("x", function(d) { 203 | return d.x0 - 6; 204 | }) 205 | .attr("y", function(d) { 206 | return d.y0 + d.dy / 2; 207 | }) 208 | .attr("dy", "0.35em") 209 | .attr("text-anchor", "end") 210 | .text(function(d) { 211 | return d.name; 212 | }) 213 | .filter(function(d) { 214 | return d.x0 < nodeWidth() 215 | }) 216 | .attr("x", function(d) { 217 | return d.x1 + 6; 218 | }) 219 | .attr("text-anchor", "start"); 220 | }) 221 | 222 | })(); 223 | -------------------------------------------------------------------------------- /charts/barChart.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // A multiple bar chart 4 | 5 | // The Model 6 | // The model abstraction is a matrix of categories: the main dimansion will define the groups, 7 | // and the secondary will define the single bars. 8 | // Optional dimension is on the bar chart color (to be defined). 9 | 10 | var model = raw.model(); 11 | 12 | // Categories dimension. each category will define a bar 13 | // It can accept both numbers and strings 14 | var categories = model.dimension() 15 | .title('X Axis') 16 | .types(Number, String) 17 | .required(true) 18 | // Values dimension. It will define the height of the bars 19 | var sizes = model.dimension() 20 | .title('Height') 21 | .types(Number) 22 | 23 | // Group dimension. 24 | // It can accept both numbers and strings 25 | var groups = model.dimension() 26 | .title('Groups') 27 | .types(Number, String) 28 | 29 | 30 | // Colors dimension. It will define the color of the bars 31 | var colorsDimesion = model.dimension() 32 | .title('Colors') 33 | .types(String) 34 | 35 | // Mapping function 36 | // For each record in the data returns the values 37 | // for the X and Y dimensions and casts them as numbers 38 | model.map(function(data) { 39 | 40 | var results = d3.nest() 41 | .key(function(d) { 42 | return d[groups()] 43 | }) 44 | .key(function(d) { 45 | return d[categories()] 46 | }) 47 | .rollup(function(v) { 48 | return { 49 | size: !sizes() ? v.length : d3.sum(v, function(e) { 50 | return e[sizes()] 51 | }), 52 | category: categories(v[0]), 53 | group: groups(v[0]), 54 | color: colorsDimesion(v[0]) 55 | } 56 | }) 57 | .entries(data) 58 | 59 | // remap the array 60 | results.forEach(function(d) { 61 | d.values = d.values.map(function(item) { 62 | return item.value 63 | }) 64 | }) 65 | 66 | return results; 67 | }) 68 | 69 | 70 | // The Chart 71 | 72 | var chart = raw.chart() 73 | .title("Bar chart") 74 | .description("A bar chart or bar graph is a chart or graph that presents grouped data with rectangular bars with heights proportional to the values that they represent.
Chart based on https://bl.ocks.org/mbostock/3310560") 75 | .thumbnail("imgs/barChart.png") 76 | .category('Other') 77 | .model(model) 78 | 79 | // visualiziation options 80 | // Width 81 | var width = chart.number() 82 | .title('Width') 83 | .defaultValue(800) 84 | 85 | // Height 86 | var height = chart.number() 87 | .title('Height') 88 | .defaultValue(600) 89 | 90 | //left margin 91 | var marginLeft = chart.number() 92 | .title('Left Margin') 93 | .defaultValue(40) 94 | 95 | // Space between barcharts 96 | var padding = chart.number() 97 | .title('Vertical padding') 98 | .defaultValue(0); 99 | 100 | // Padding between bars 101 | var xPadding = chart.number() 102 | .title('Horizontal padding') 103 | .defaultValue(0.1); 104 | 105 | // Use or not the same scale across all the bar charts 106 | var sameScale = chart.checkbox() 107 | .title("Use same scale") 108 | .defaultValue(false) 109 | 110 | // Chart colors 111 | var colors = chart.color() 112 | .title("Color scale") 113 | 114 | // Drawing function 115 | // selection represents the d3 selection (svg) 116 | // data is not the original set of records 117 | // but the result of the model map function 118 | chart.draw(function(selection, data) { 119 | 120 | // Define margins 121 | var margin = { 122 | top: 0, 123 | right: 0, 124 | bottom: 50, 125 | left: marginLeft() 126 | }; 127 | //define title space 128 | var titleSpace = groups() == null ? 0 : 30; 129 | 130 | // Define common variables. 131 | // Find the overall maximum value 132 | var maxValue; 133 | 134 | if (sameScale()) { 135 | maxValue = d3.max(data, function(item) { 136 | return d3.max(item.values, function(d) { 137 | return d.size; 138 | }); 139 | }) 140 | } 141 | 142 | // Check consistency among categories and colors, save them all 143 | var allCategories = []; 144 | var allColors = []; 145 | data.forEach(function(item) { 146 | 147 | var temp_categories = item.values.map(function(val) { 148 | return val.category; 149 | }) 150 | allCategories = allCategories.concat(temp_categories); 151 | 152 | // Same for color 153 | var temp_colors = item.values.map(function(val) { 154 | return val.color; 155 | }) 156 | allColors = allColors.concat(temp_colors); 157 | }) 158 | //keep uniques 159 | allCategories = d3.set(allCategories).values(); 160 | allColors = d3.set(allColors).values(); 161 | 162 | // svg size 163 | selection 164 | .attr("width", width()) 165 | .attr("height", height()) 166 | 167 | // define single barchart height, 168 | // depending on the number of bar charts 169 | var w = +width() - margin.left, 170 | h = (+height() - margin.bottom - ((titleSpace + padding()) * (data.length - 1))) / data.length; 171 | 172 | 173 | // Define scales 174 | var xScale = d3.scaleBand() 175 | .rangeRound([0, w]) 176 | .padding(+xPadding()); 177 | 178 | var yScale = d3.scaleLinear() 179 | .range([h, 0]); 180 | 181 | // Define color scale domain 182 | colors.domain(allColors); 183 | 184 | // Draw each bar chart 185 | data.forEach(function(item, index) { 186 | 187 | // Define x domain 188 | xScale.domain(allCategories); 189 | // Define y domain 190 | if (sameScale()) { 191 | yScale.domain([0, maxValue]); 192 | } else { 193 | yScale.domain([0, d3.max(item.values, function(d) { 194 | return d.size; 195 | })]); 196 | } 197 | 198 | // Append a grupo containing axis and bars, 199 | // move it according the index 200 | barchart = selection.append("g") 201 | .attr("transform", "translate(" + margin.left + "," + index * (h + padding() + titleSpace) + ")"); 202 | 203 | // Draw title 204 | barchart.append("text") 205 | .attr("x", -margin.left) 206 | .attr("y", titleSpace - 7) 207 | .style("font-size", "10px") 208 | .style("font-family", "Arial, Helvetica") 209 | .text(item.key); 210 | 211 | // Draw y axis 212 | barchart.append("g") 213 | .attr("class", "y axis") 214 | .style("font-size", "10px") 215 | .style("font-family", "Arial, Helvetica") 216 | .attr("transform", "translate(0," + titleSpace + ")") 217 | .call(d3.axisLeft(yScale).ticks(h / 15)); 218 | 219 | // Draw the bars 220 | barchart.selectAll(".bar") 221 | .data(item.values) 222 | .enter().append("rect") 223 | .attr("transform", "translate(0," + titleSpace + ")") 224 | .attr("class", "bar") 225 | .attr("x", function(d) { 226 | return xScale(d.category); 227 | }) 228 | .attr("width", xScale.bandwidth()) 229 | .attr("y", function(d) { 230 | return yScale(d.size); 231 | }) 232 | .attr("height", function(d) { 233 | return h - yScale(d.size); 234 | }) 235 | .style("fill", function(d) { 236 | return colors()(d.color); 237 | }); 238 | 239 | }) 240 | 241 | // After all the charts, draw x axis 242 | selection.append("g") 243 | .attr("class", "x axis") 244 | .style("font-size", "10px") 245 | .style("font-family", "Arial, Helvetica") 246 | .attr("transform", "translate(" + margin.left + "," + ((h + padding() + titleSpace) * data.length - padding()) + ")") 247 | .call(d3.axisBottom(xScale)); 248 | 249 | 250 | // Set styles 251 | 252 | d3.selectAll(".axis line, .axis path") 253 | .style("shape-rendering", "crispEdges") 254 | .style("fill", "none") 255 | .style("stroke", "#ccc"); 256 | 257 | }) 258 | })(); 259 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![raw header](imgs/raw_header.jpg) 2 | 3 | ## About 4 | 5 | **RAWGraphs** is an open web tool 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/). 6 | It has been developed by [DensityDesign Research Lab](http://www.densitydesign.org/) ([Politecnico di Milano](http://www.polimi.it/)) and [Calibro](http://calib.ro/), and sustained through a corporate stewardship by [ContactLab](http://contactlab.com/it/). 7 | 8 | Primarily conceived as a tool for designers and vis geeks, RAWGraphs 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, …). 9 | 10 | RAWGraphs works with [tabular data](https://en.wikipedia.org/wiki/Table_(information)) (e.g. spreadhseets and comma-separated values) 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. 11 | 12 | Knowing the need of working with sensitive information, the data injected into RAWGraphs 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! 13 | 14 | RAWGraphs 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/rawgraphs/raw/wiki/Developer-Guide). 15 | 16 | - App page: [app.rawgraphs.io](http://app.rawgraphs.io) 17 | - Project official page: [rawgraphs.io](http://rawgraphs.io) 18 | - Documentation: [github.com/rawgraphs/raw/wiki](https://github.com/rawgraphs/raw/wiki) 19 | - Google group: [groups.google.com/forum/#!forum/densitydesign-raw](https://groups.google.com/forum/#!forum/densitydesign-raw) 20 | 21 | 22 | ## Usage 23 | The easiest way to use RAWGraphs is by accessing the most updated version on the **[official app page](http://app.rawgraphs.io)**. However, RAWGraphs can also run locally on your machine: see the installation instructions below to know how. 24 | 25 | ## Installation 26 | If you want to run your instance of RAWGraphs locally on your machine, be sure you have the following requirements installed. 27 | 28 | ### Requirements 29 | 30 | - [git](http://git-scm.com/book/en/Getting-Started-Installing-Git) 31 | - [Bower](http://bower.io/#installing-bower) 32 | 33 | ### Instructions 34 | 35 | Clone RAWGraphs from the command line: 36 | 37 | ``` sh 38 | $ git clone https://github.com/rawgraphs/raw.git 39 | ``` 40 | 41 | browse to RAWGraphs root folder: 42 | 43 | ``` sh 44 | $ cd raw 45 | ``` 46 | 47 | install client-side dependencies: 48 | 49 | ``` sh 50 | $ bower install 51 | ``` 52 | 53 | add analytics script: 54 | 55 | ``` sh 56 | $ cp js/analytics.sample.js js/analytics.js 57 | ``` 58 | 59 | open the file ```js/analytics.js``` and add your analytics code (if any), otherwise leave the file as is. 60 | 61 | You can now run RAWGraphs from your local web server. For example, you can run Python's built-in server: 62 | 63 | ``` sh 64 | $ python -m SimpleHTTPServer 4000 65 | ``` 66 | 67 | or for Python 3+ 68 | 69 | ``` sh 70 | $ python -m http.server 4000 71 | ``` 72 | 73 | Once this is running, go to [http://localhost:4000/](http://localhost:4000/). 74 | 75 | Troubles with the installation? Maybe a look at the [issues](https://github.com/rawgraphs/raw/issues) page can solve your problem, otherwise join the [Google group](https://groups.google.com/forum/#!forum/densitydesign-raw). 76 | 77 | 78 | ## Documentation and Support 79 | 80 | Documentation and FAQs about how to use RAWGraphs can be found on the [wiki](https://github.com/rawgraphs/raw/wiki/). 81 | 82 | ## Charts 83 | 84 | Information about the available charts can be found [here](https://github.com/rawgraphs/raw/wiki/Available-Charts). Adding new charts is very easy in RAWGraphs, see how [here](https://github.com/rawgraphs/raw/wiki/Adding-New-Charts)! 85 | 86 | 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/rawgraphs/raw/pulls). 87 | 88 | ## Libraries 89 | 90 | **RAWGraphs** has been developed using a lot of cool stuff found out there: 91 | 92 | [angular.js](https://github.com/angular/angular.js) 93 | 94 | [angular-bootstrap-colorpicker](https://github.com/buberdds/angular-bootstrap-colorpicker) 95 | 96 | [angular-ui](https://github.com/angular-ui) 97 | 98 | [bootstrap](https://github.com/twbs/bootstrap) 99 | 100 | [bootstrap-colorpicker](http://www.eyecon.ro/bootstrap-colorpicker/) 101 | 102 | [Bower](https://github.com/bower/bower) 103 | 104 | [canvas-toBlob.js](https://github.com/eligrey/canvas-toBlob.js) 105 | 106 | [CodeMirror](https://github.com/marijnh/codemirror) 107 | 108 | [d3.js](https://github.com/mbostock/d3) 109 | 110 | [d3-legend](https://github.com/susielu/d3-legend) 111 | 112 | [FileSaver.js](https://github.com/eligrey/FileSaver.js) 113 | 114 | [is.js](http://is.js.org/) 115 | 116 | [jQuery](https://github.com/jquery/jquery) 117 | 118 | [jQuery UI Touch Punch](https://github.com/furf/jquery-ui-touch-punch/) 119 | 120 | [NG file upload](https://github.com/danialfarid/ng-file-upload) 121 | 122 | [Sheet JS](https://github.com/SheetJS) 123 | 124 | [ZeroClipboard](https://github.com/zeroclipboard/zeroclipboard) 125 | 126 | ## Core Team 127 | 128 | **RAWGraphs** is maintained by [DensityDesign Research Lab](http://www.densitydesign.org/) and [Calibro](http://calib.ro/). 129 | 130 | If you want to know more about RAWGraphs, how it works and future developments, please visit the [official website](http://rawgraphs.io). 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 . 131 | 132 | ## Contributing 133 | 134 | Want to contribute to RAWGraphs'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. 135 | You will need to sign a [Contributor License Agreement (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) before making a submission. We adopted CLA to be sure that the project will remain open source. For more information, write us: . 136 | 137 | ## Publications / Citing RAWGraphs 138 | If you have found RAWGraphs useful in your research, or if you want to reference it in your work, please consider to cite the paper we presented at [CHItaly 2017](http://sites.unica.it/chitaly2017/). 139 | 140 | you can read the full article in Green Open Access at the following link: 141 | 142 | ![oa icon](http://dl.acm.org/images/oa.gif) [RAWGraphs: A Visualisation Platform to Create Open Outputs](http://rawgraphs.io/about/#cite) 143 | 144 | Cite RAWGraphs: 145 | 146 | > Mauri, M., Elli, T., Caviglia, G., Uboldi, G., & Azzi, M. (2017). RAWGraphs: A Visualisation Platform to Create Open Outputs. In *Proceedings of the 12th Biannual Conference on Italian SIGCHI Chapter* (p. 28:1–28:5). New York, NY, USA: ACM. https://doi.org/10.1145/3125571.3125585 147 | 148 | Bibtex: 149 | ``` 150 | @inproceedings{Mauri:2017:RVP:3125571.3125585, 151 | author = {Mauri, Michele and Elli, Tommaso and Caviglia, Giorgio and Uboldi, Giorgio and Azzi, Matteo}, 152 | title = {RAWGraphs: A Visualisation Platform to Create Open Outputs}, 153 | booktitle = {Proceedings of the 12th Biannual Conference on Italian SIGCHI Chapter}, 154 | series = {CHItaly '17}, 155 | year = {2017}, 156 | isbn = {978-1-4503-5237-6}, 157 | location = {Cagliari, Italy}, 158 | pages = {28:1--28:5}, 159 | articleno = {28}, 160 | numpages = {5}, 161 | url = {http://doi.acm.org/10.1145/3125571.3125585}, 162 | doi = {10.1145/3125571.3125585}, 163 | acmid = {3125585}, 164 | publisher = {ACM}, 165 | address = {New York, NY, USA}, 166 | keywords = {Visualization tools, data visualization, open output, visual interface}, 167 | } 168 | ``` 169 | 170 | ## Authors 171 | **RAWGraphs** has been originally developed by: 172 | 173 | * Giorgio Caviglia 174 | * Michele Mauri 175 | * Giorgio Uboldi 176 | * Matteo Azzi 177 | 178 | ## License 179 | 180 | RAWGraphs is provided under the [Apache License 2.0](https://github.com/rawgraphs/raw/blob/master/LICENSE): 181 | 182 | Copyright (c), 2013-2019 DensityDesign Lab, Giorgio Caviglia, Michele Mauri, Giorgio Uboldi, Matteo Azzi 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 191 | You may obtain a copy of the License at 192 | 193 | http://www.apache.org/licenses/LICENSE-2.0 194 | 195 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 196 | See the License for the specific language governing permissions and limitations under the License. 197 | -------------------------------------------------------------------------------- /charts/gantt.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // The Model 4 | var timechuncks = raw.model(); 5 | 6 | // Groups. It defines how to aggregate thte data 7 | var group = timechuncks.dimension('groups') 8 | .title('Groups') 9 | 10 | // Start Date. It will define the starting point of each bar 11 | var startDate = timechuncks.dimension('startDate') 12 | .title('Start date') 13 | .types(Date) 14 | .accessor(function(d) { 15 | return this.type() == "Date" ? Date.parse(d) : +d; 16 | }) 17 | .required(true) 18 | 19 | // End Date. It will define the ending point of each bar 20 | var endDate = timechuncks.dimension('endDate') 21 | .title('End date') 22 | .types(Date) 23 | .accessor(function(d) { 24 | return this.type() == "Date" ? Date.parse(d) : +d; 25 | }) 26 | .required(true) 27 | 28 | // Colors. it defines the color of each bar. 29 | var colorDimension = timechuncks.dimension('color') 30 | .title('Colors') 31 | .types(String) 32 | 33 | // Mapping function 34 | // For each record in the data returns the values 35 | // for the X and Y dimensions and casts them as numbers 36 | timechuncks.map(function(data) { 37 | 38 | var level = id = 0; 39 | 40 | var nest = d3.nest() 41 | .key(function(d) { 42 | return group() ? group(d) : '' 43 | }) 44 | .rollup(function(v) { 45 | 46 | v.sort(function(a, b) { 47 | return d3.ascending(startDate(a), startDate(b)) 48 | }) 49 | var l, levels = []; 50 | level = 0; 51 | v.forEach(function(item, i) { 52 | l = 0; 53 | while (overlap(item, levels[l])) l++; 54 | if (!levels[l]) levels[l] = []; 55 | levels[l].push({ 56 | level: l + level, 57 | id: id++, 58 | color: colorDimension(item), 59 | group: group() ? group(item) : "", 60 | start: startDate(item), 61 | end: endDate(item), 62 | data: item 63 | }); 64 | }) 65 | 66 | level++; 67 | return levels; 68 | }) 69 | .entries(data); 70 | 71 | function overlap(item, g) { 72 | if (!g) return false; 73 | for (var i in g) { 74 | if (startDate(item) < g[i].end && endDate(item) > g[i].start) { 75 | return true; 76 | } 77 | } 78 | return false; 79 | }; 80 | 81 | return nest; 82 | }) 83 | 84 | 85 | // The Chart 86 | var chart = raw.chart() 87 | .title('Gantt Chart') 88 | .thumbnail("imgs/gantt.png") 89 | .description("A Gantt chart is a type of bar chart, developed by Henry Gantt in the 1910s, that illustrates a project schedule. Gantt charts illustrate the start and finish dates of the terminal elements and summary elements of a project.") 90 | .category('Time chunks') 91 | .model(timechuncks) 92 | 93 | // OPTIONS 94 | 95 | // Width 96 | var width = chart.number() 97 | .title('Width') 98 | .defaultValue(900) 99 | 100 | // Height 101 | var height = chart.number() 102 | .title('Height') 103 | .defaultValue(600) 104 | 105 | //left margin 106 | var marginLeft = chart.number() 107 | .title('Left Margin') 108 | .defaultValue(80) 109 | 110 | //labels horizontal alignment 111 | var alignment = chart.checkbox() 112 | .title("Align labels to bar") 113 | .defaultValue(false); 114 | 115 | // sorting options 116 | var sort = chart.list() 117 | .title("Sort by") 118 | .values(['Start date (ascending)', 'Start date (descending)', 'Name']) 119 | .defaultValue('Start date (ascending)') 120 | 121 | // Colors for the chart 122 | var colors = chart.color() 123 | .title("Color scale") 124 | 125 | // Drawing function 126 | chart.draw(function(selection, data) { 127 | 128 | // svg size, create container group 129 | var g = selection 130 | .attr("width", width()) 131 | .attr("height", height()) 132 | .append('g') 133 | 134 | //define margins 135 | var margin = { 136 | top: 10, 137 | right: 0, 138 | bottom: 20, 139 | left: marginLeft() 140 | }; 141 | 142 | //compute the total amount of levels 143 | var levels = d3.sum(data.map(function(d) { 144 | return d.value.length; 145 | })); 146 | 147 | //re-flatten hierarchy 148 | var entries = [] 149 | 150 | data.forEach(function(d) { 151 | d.value.forEach(function(e) { 152 | entries = entries.concat(e) 153 | }) 154 | }) 155 | 156 | //get the list of single colors 157 | var allColors = d3.set(entries.map(function(d) { 158 | return d.color; 159 | })).values(); 160 | 161 | //define the colors domain 162 | colors.domain(allColors); 163 | 164 | //define x scale 165 | var x = d3.scaleTime() 166 | .range([margin.left, width()]) 167 | .domain([ 168 | d3.min(entries, function(d) { 169 | return d.start; 170 | }), 171 | d3.max(entries, function(d) { 172 | return d.end; 173 | }) 174 | ]); 175 | 176 | //create x axis 177 | var xAxis = d3.axisBottom(x); 178 | 179 | //compute items height 180 | var itemHeight = (height() - margin.top - margin.bottom) / levels; 181 | 182 | //create items 183 | var newPosition = margin.top; 184 | 185 | var items = g.selectAll('g.itemGroup') 186 | .data(data.sort(sortBy)) 187 | .enter().append('g') 188 | .attr("class", "itemGroup") 189 | .attr("transform", function(d, i) { 190 | var currentPosition = newPosition; 191 | newPosition += d.value.length * itemHeight; 192 | return 'translate(0, ' + currentPosition + ')'; 193 | }); 194 | 195 | //add horizontal lines 196 | items.append('line') 197 | .attr('x1', margin.left) 198 | .attr('y1', 0) 199 | .attr('x2', width()) 200 | .attr('y2', 0) 201 | .style('shape-rendering', 'crispEdges') 202 | .attr('stroke', 'lightgrey'); 203 | 204 | //add groups labels 205 | items.append('text') 206 | .text(function(d) { 207 | return d.key 208 | }) 209 | .style("font-size", "11px") 210 | .style("font-family", "Arial, Helvetica") 211 | .attr('x', function(d) { 212 | return alignment() ? x(d.value[0][0].start) - 10 : margin.left - 10 213 | }) 214 | .attr('y', 0) 215 | .attr('dy', function(d) { 216 | //half of the size plus 4px (custom value workin with the chosen font size) 217 | return itemHeight / 2 + 4; 218 | }) 219 | .attr('text-anchor', 'end') 220 | .attr('class', 'groupText'); 221 | 222 | // draw rectangles 223 | items.selectAll('rect') 224 | .data(function(d) { // take the data and return the list. it should be changed in the model. 225 | var p = []; 226 | d.value.forEach(function(dd) { 227 | p = p.concat(dd); 228 | }); 229 | return p; 230 | }) 231 | .enter().append('rect') 232 | .attr('x', function(d) { 233 | return x(d.start); 234 | }) 235 | .attr('y', function(d) { 236 | return itemHeight * d.level; 237 | }) 238 | .attr('width', function(d) { 239 | return d3.max([1, x(d.end) - x(d.start)]); 240 | }) 241 | .attr('height', itemHeight) 242 | .style("shape-rendering", "crispEdges") 243 | .style('fill', function(d) { 244 | return colors()(d.color); 245 | }) 246 | 247 | //add axes 248 | g.append('g') 249 | .attr('transform', `translate(0, ${margin.top + itemHeight * levels})`) 250 | .attr("class", "axis") 251 | .style("stroke-width", "1px") 252 | .style("font-size", "10px") 253 | .style("font-family", "Arial, Helvetica") 254 | .call(xAxis); 255 | 256 | d3.selectAll(".axis line, .axis path") 257 | .style("shape-rendering", "crispEdges") 258 | .style("fill", "none") 259 | .style("stroke", "#ccc") 260 | 261 | }) 262 | 263 | //sorting functions 264 | function sortBy(a, b) { 265 | if (sort() == 'Start date (descending)') return d3.descending(a.value[0][0].start, b.value[0][0].start) 266 | if (sort() == 'Start date (ascending)') return d3.ascending(a.value[0][0].start, b.value[0][0].start) 267 | // if (sort() == 'End date (descending)') return d3.descending(a.value[0][0].end, b.value[0][0].end) 268 | // if (sort() == 'End date (ascending)') return d3.ascending(a.value[0][0].end, b.value[0][0].end) 269 | if (sort() == 'Name') return d3.ascending(a.key, b.key) 270 | } 271 | 272 | })(); 273 | -------------------------------------------------------------------------------- /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(+Date.parse(d)) : this.type() == "String" ? d : +d; }) 13 | .accessor(function(d) { return +Date.parse(d) }) 14 | .required(1) 15 | 16 | var size = stream.dimension() 17 | .title('Size') 18 | .types(Number) 19 | 20 | stream.map(function(data) { 21 | if (!group()) return []; 22 | 23 | var keys = d3.set(data.map(function(d) { return group(d); })).values(); 24 | 25 | var groups = d3.nest() 26 | .key(function(d) { return date(d) }).sortKeys(function(a, b) { return a - b }) 27 | .rollup(function(v) { 28 | var singles = d3.nest() 29 | .key(function(e) { return group(e) }) 30 | .rollup(function(w) { 31 | return { 32 | size: +d3.sum(w, function(f) { return size() ? size(f) : 1 }), 33 | date: +date(w[0]), 34 | group: group(w[0]) 35 | } 36 | }) 37 | .entries(v); 38 | 39 | var map = d3.map(singles, function(d) { return d.key }); 40 | 41 | keys.forEach(function(d) { 42 | if (!map.has(d)) { 43 | 44 | singles.push({ 45 | 'key': d, 46 | 'value': { size: 0, date: d, group: group(v[0]) } 47 | }) 48 | } 49 | }); 50 | 51 | return singles.sort(function(a, b) { return d3.ascending(a.key, b.key) }); 52 | }) 53 | .entries(data) 54 | 55 | var results = groups.map(function(d) { 56 | var item = {} 57 | d.value.forEach(function(e) { 58 | item[e.key] = e.value['size'] 59 | }); 60 | item.date = new Date(+d.key); 61 | return item; 62 | }); 63 | 64 | //create a wrap object 65 | var output = { 66 | values: results, 67 | keys: keys 68 | } 69 | 70 | return output 71 | }) 72 | 73 | var chart = raw.chart() 74 | .title('Streamgraph') 75 | .thumbnail("imgs/streamgraph.png") 76 | .description( 77 | "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") 78 | .category('Time series') 79 | .model(stream) 80 | 81 | var width = chart.number() 82 | .title("Width") 83 | .defaultValue(1000) 84 | .fitToWidth(true) 85 | 86 | var height = chart.number() 87 | .title("Height") 88 | .defaultValue(500) 89 | 90 | var order = chart.list() 91 | .title("Series order") 92 | .values(["Original order", "Bigger to smaller", "Smaller to bigger", "Inside out", "Reverse original"]) 93 | .defaultValue("Original order") 94 | 95 | var offset = chart.list() 96 | .title("Offset") 97 | .values(['Expand', 'Diverging', 'Silhouette', 'Wiggle', 'None']) 98 | .defaultValue('Wiggle') 99 | 100 | var curve = chart.list() 101 | .title("Interpolation") 102 | .values(['Cardinal', 'Basis spline', 'DensityDesign', 'Linear']) 103 | .defaultValue('Basis spline') 104 | 105 | var showLabels = chart.checkbox() 106 | .title("Show labels") 107 | .defaultValue(false) 108 | 109 | var colors = chart.color() 110 | .title("Color scale") 111 | 112 | chart.draw(function(selection, data) { 113 | 114 | var g = selection 115 | .attr("width", +width()) 116 | .attr("height", +height()) 117 | .append("g") 118 | 119 | var curves = { 120 | 'Basis spline': d3.curveBasis, 121 | 'Cardinal': d3.curveCardinal, 122 | 'Linear': d3.curveLinear, 123 | 'DensityDesign': curveSankey 124 | } 125 | 126 | var offsets = { 127 | 'Expand': d3.stackOffsetExpand, 128 | 'Diverging': d3.stackOffsetDiverging, 129 | 'None': d3.stackOffsetNone, 130 | 'Silhouette': d3.stackOffsetSilhouette, 131 | 'Wiggle': d3.stackOffsetWiggle 132 | } 133 | 134 | var orders = { 135 | "Original order": d3.stackOrderNone, 136 | "Bigger to smaller": d3.stackOrderAscending, 137 | "Smaller to bigger": d3.stackOrderDescending, 138 | "Inside out": d3.stackOrderInsideOut, 139 | "Reverse original": d3.stackOrderReverse 140 | } 141 | 142 | var stack = d3.stack() 143 | .keys(data.keys) 144 | .offset(offsets[offset()]) 145 | .order(orders[order()]); 146 | 147 | var layers = stack(data.values); 148 | 149 | //below, the old code to swap among three types of data. for now, it is forced to dates only. 150 | 151 | // var x = date.type() == "Date" 152 | // // Date 153 | // ? d3.scaleTime() 154 | // .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; }); }) ]) 155 | // .range([0, +width()]) 156 | // : date && date.type() == "String" 157 | // // String 158 | // ? d3.scaleOrdinal() 159 | // .domain(layers[0].map(function(d){ return d.x; }) ) 160 | // .rangePoints([0, +width()],0) 161 | // // Number 162 | // : d3.scaleLinear() 163 | // .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; }); }) ]) 164 | // .range([0, +width()]); 165 | 166 | // var y = d3.scaleLinear() 167 | // .domain([0, d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); })]) 168 | // .range([+height()-20, 0]); 169 | 170 | var x = d3.scaleTime() 171 | .domain(d3.extent(data.values, function(d) { return d.date; })) 172 | .range([0, +width()]); 173 | 174 | var y = d3.scaleLinear() 175 | .domain([d3.min(layers, stackMin), d3.max(layers, stackMax)]) 176 | .range([+height() - 20, 0]); 177 | 178 | function stackMax(layer) { 179 | return d3.max(layer, function(d) { return d[1]; }); 180 | } 181 | 182 | function stackMin(layer) { 183 | return d3.min(layer, function(d) { return d[0]; }); 184 | } 185 | 186 | colors.domain(data.keys); 187 | 188 | var xAxis = d3.axisBottom(x); //.tickSize(-height()+20); 189 | 190 | var area = d3.area() 191 | .curve(curves[curve()]) 192 | .x(function(d) { return x(d.data.date); }) 193 | .y0(function(d) { return y(d[0]); }) 194 | .y1(function(d) { return y(d[1]); }); 195 | 196 | var line = d3.line() 197 | .curve(curves[curve()]) 198 | .x(function(d) { return x(d.data.date); }) 199 | .y(function(d) { 200 | return y(d[0] + (d[1] - d[0]) / 2); 201 | }); 202 | 203 | g.selectAll("path.layer") 204 | .data(layers) 205 | .enter().append("path") 206 | .attr("class", "layer") 207 | .attr("d", area) 208 | .attr("fill", function(d) { return colors()(d.key) }) 209 | .attr("title", function(d) { return d.key; }); 210 | 211 | g.append("g") 212 | .attr("class", "x axis") 213 | .style("stroke-width", "1px") 214 | .style("font-size", "10px") 215 | .style("font-family", "Arial, Helvetica") 216 | .attr("transform", "translate(" + 0 + "," + (height() - 20) + ")") 217 | .call(xAxis); 218 | 219 | d3.selectAll(".x.axis line, .x.axis path") 220 | .style("shape-rendering", "crispEdges") 221 | .style("fill", "none") 222 | .style("stroke", "#ccc") 223 | 224 | if (!showLabels()) return; 225 | 226 | g.append('defs'); 227 | 228 | g.select('defs') 229 | .selectAll('path') 230 | .data(layers) 231 | .enter().append('path') 232 | .attr('id', function(d, i) { return 'path-' + i; }) 233 | .attr('d', line); 234 | 235 | g.selectAll("text.label") 236 | .data(layers) 237 | .enter().append('text') 238 | .attr('dy', '0.5ex') 239 | .attr("class", "label") 240 | .append('textPath') 241 | .attr('xlink:xlink:href', function(d, i) { return '#path-' + i; }) 242 | .attr('startOffset', function(d) { 243 | var maxYloc = 0, 244 | maxV = 0; 245 | d.forEach(function(e, i) { 246 | // get point with max value 247 | var val = e[1] - e[0]; 248 | if (val > maxV) { 249 | maxV = val; 250 | maxYloc = i 251 | } 252 | }) 253 | //get y position 254 | d.offset = Math.round(maxYloc / d.length * 100); 255 | //return offset 256 | return Math.min(95, Math.max(5, d.offset)) + '%'; 257 | }) 258 | .attr('text-anchor', function(d) { 259 | return d.offset > 90 ? 'end' : d.offset < 10 ? 'start' : 'middle'; 260 | }) 261 | .text(function(d) { return d.key; }) 262 | .style("font-size", "11px") 263 | .style("font-family", "Arial, Helvetica") 264 | .style("font-weight", "normal") 265 | 266 | /*g.selectAll("text.label") 267 | .data(labels(layers)) 268 | .enter().append("text") 269 | .attr("x", function(d){ return x(d.x); }) 270 | .attr("y", function(d){ return y(d.y0 + d.y/2); }) 271 | .text(function(d){ return d.group; }) 272 | .style("font-size","11px") 273 | .style("font-family","Arial, Helvetica") 274 | 275 | 276 | 277 | function labels(layers){ 278 | return layers.map(function(layer){ 279 | var l = layer[0], max = 0; 280 | layer.forEach(function(d){ 281 | if ( d.y > max ) { 282 | max = d.y; 283 | l = d; 284 | } 285 | }) 286 | return l; 287 | }) 288 | 289 | }*/ 290 | 291 | }) 292 | 293 | //Sankey interpolator. Could be moved in a better place. 294 | function CurveSankey(context) { 295 | this._context = context; 296 | } 297 | 298 | CurveSankey.prototype = { 299 | areaStart: function() { 300 | this._line = 0; 301 | }, 302 | areaEnd: function() { 303 | this._line = NaN; 304 | }, 305 | lineStart: function() { 306 | this._x = this._y = NaN; 307 | this._point = 0; 308 | }, 309 | lineEnd: function() { 310 | if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); 311 | this._line = 1 - this._line; 312 | }, 313 | point: function(x, y) { 314 | x = +x, y = +y; 315 | switch (this._point) { 316 | case 0: 317 | this._point = 1; 318 | this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); 319 | break; 320 | case 1: 321 | this._point = 2; // proceed 322 | default: 323 | var mx = (x - this._x) / 2 + this._x; 324 | this._context.bezierCurveTo(mx, this._y, mx, y, x, y); 325 | break; 326 | } 327 | this._x = x, this._y = y; 328 | } 329 | }; 330 | 331 | var curveSankey = function(context) { 332 | return new CurveSankey(context); 333 | } 334 | 335 | })(); 336 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /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(+Date.parse(d)) : this.type() == "String" ? d : +d; }) 13 | .accessor(function(d) { return +Date.parse(d) }) 14 | .required(1) 15 | 16 | var size = stream.dimension() 17 | .title('Size') 18 | .types(Number) 19 | 20 | stream.map(function(data) { 21 | if (!group()) return []; 22 | 23 | var keys = d3.set(data.map(function(d) { return group(d); })).values(); 24 | 25 | var groups = d3.nest() 26 | .key(function(d) { return date(d) }).sortKeys(function(a, b) { return a - b }) 27 | .rollup(function(v) { 28 | var singles = d3.nest() 29 | .key(function(e) { return group(e) }) 30 | .rollup(function(w) { 31 | return { 32 | size: +d3.sum(w, function(f) { return size() ? size(f) : 1 }), 33 | date: +date(w[0]), 34 | group: group(w[0]) 35 | } 36 | }) 37 | .entries(v); 38 | 39 | var map = d3.map(singles, function(d) { return d.key }); 40 | 41 | keys.forEach(function(d) { 42 | if (!map.has(d)) { 43 | 44 | singles.push({ 45 | 'key': d, 46 | 'value': { size: 0, date: d, group: group(v[0]) } 47 | }) 48 | } 49 | }); 50 | 51 | return singles.sort(function(a, b) { return d3.ascending(a.key, b.key) }); 52 | }) 53 | .entries(data) 54 | 55 | var results = groups.map(function(d) { 56 | var item = {} 57 | d.value.forEach(function(e) { 58 | item[e.key] = e.value['size'] 59 | }); 60 | item.date = new Date(+d.key); 61 | return item; 62 | }); 63 | 64 | //create a wrap object 65 | var output = { 66 | values: results, 67 | keys: keys 68 | } 69 | 70 | return output 71 | }) 72 | 73 | var chart = raw.chart() 74 | .title('Bump Chart') 75 | .thumbnail("imgs/bumpChart.png") 76 | .description( 77 | "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.") 78 | .category('Time series') 79 | .model(stream) 80 | 81 | var width = chart.number() 82 | .title("Width") 83 | .defaultValue(1000) 84 | .fitToWidth(true) 85 | 86 | var height = chart.number() 87 | .title("Height") 88 | .defaultValue(500) 89 | 90 | var padding = chart.number() 91 | .title("Padding") 92 | .defaultValue(0) 93 | 94 | var normalize = chart.checkbox() 95 | .title("Normalize") 96 | .defaultValue(false) 97 | 98 | var sort = chart.list() 99 | .title("Sort by") 100 | .values(['value (descending)', 'value (ascending)']) 101 | .defaultValue('value (descending)') 102 | 103 | var curve = chart.list() 104 | .title("Interpolation") 105 | .values(['Cardinal', 'Basis spline', 'DensityDesign', 'Linear']) 106 | .defaultValue('DensityDesign') 107 | 108 | var showLabels = chart.checkbox() 109 | .title("Show labels") 110 | .defaultValue(false) 111 | 112 | var colors = chart.color() 113 | .title("Color scale") 114 | 115 | chart.draw(function(selection, data) { 116 | 117 | var g = selection 118 | .attr("width", +width()) 119 | .attr("height", +height()) 120 | .append("g") 121 | 122 | var curves = { 123 | 'Basis spline': d3.curveBasis, 124 | 'Cardinal': d3.curveCardinal, 125 | 'Linear': d3.curveLinear, 126 | 'DensityDesign': curveSankey 127 | } 128 | 129 | var offsets = { 130 | 'Expand': d3.stackOffsetExpand, 131 | 'Diverging': d3.stackOffsetDiverging, 132 | 'None': d3.stackOffsetNone, 133 | 'Silhouette': d3.stackOffsetSilhouette, 134 | 'Wiggle': d3.stackOffsetWiggle 135 | } 136 | 137 | var stack = d3.stack() 138 | .keys(data.keys) 139 | .offset(normalize() ? d3.stackOffsetExpand : d3.stackOffsetNone); 140 | 141 | var layers = stack(data.values); 142 | 143 | var x = d3.scaleTime() 144 | .domain(d3.extent(data.values, function(d) { return d.date; })) 145 | .range([0, +width()]); 146 | 147 | var y = d3.scaleLinear() 148 | .domain([d3.min(layers, stackMin), d3.max(layers, stackMax)]) 149 | .range([0, +height() - 20 - padding() * (layers.length - 1)]); 150 | 151 | //sort layers by size 152 | for (var i = 0; i < layers[0].length; i++) { 153 | 154 | var values = layers.map(function(layer) { 155 | return layer[i]; 156 | }) 157 | .sort(sortBy); 158 | 159 | var sum = d3.sum(values, function(layer) { return layer[1] - layer[0]; }); 160 | 161 | var y0 = (y.domain()[1] - sum) / 2; 162 | 163 | values.forEach(function(layer, li) { 164 | var yd = layer[1] - layer[0]; 165 | layer[0] = y0; 166 | layer[1] = y0 + yd; 167 | 168 | //increase y0 169 | y0 += yd + y.invert(padding()); 170 | }); 171 | } 172 | 173 | //below, the old code to swap among three types of data. for now, it is forced to dates only. 174 | 175 | // var x = date.type() == "Date" 176 | // // Date 177 | // ? d3.scaleTime() 178 | // .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; }); }) ]) 179 | // .range([0, +width()]) 180 | // : date && date.type() == "String" 181 | // // String 182 | // ? d3.scaleOrdinal() 183 | // .domain(layers[0].map(function(d){ return d.x; }) ) 184 | // .rangePoints([0, +width()],0) 185 | // // Number 186 | // : d3.scaleLinear() 187 | // .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; }); }) ]) 188 | // .range([0, +width()]); 189 | 190 | // var y = d3.scaleLinear() 191 | // .domain([0, d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); })]) 192 | // .range([+height()-20, 0]); 193 | 194 | function stackMax(layer) { 195 | return d3.max(layer, function(d) { return d[1]; }); 196 | } 197 | 198 | function stackMin(layer) { 199 | return d3.min(layer, function(d) { return d[0]; }); 200 | } 201 | 202 | colors.domain(data.keys); 203 | 204 | var xAxis = d3.axisBottom(x); 205 | 206 | var area = d3.area() 207 | .curve(curves[curve()]) 208 | .x(function(d) { return x(d.data.date); }) 209 | .y0(function(d) { return y(d[0]); }) 210 | .y1(function(d) { return y(d[1]); }); 211 | 212 | var line = d3.line() 213 | .curve(curves[curve()]) 214 | .x(function(d) { return x(d.data.date); }) 215 | .y(function(d) { 216 | return y(d[0] + (d[1] - d[0]) / 2); 217 | }); 218 | 219 | g.selectAll("path.layer") 220 | .data(layers) 221 | .enter().append("path") 222 | .attr("class", "layer") 223 | .attr("d", area) 224 | .attr("fill", function(d) { return colors()(d.key) }) 225 | .attr("title", function(d) { return d.key; }); 226 | 227 | g.append("g") 228 | .attr("class", "x axis") 229 | .style("stroke-width", "1px") 230 | .style("font-size", "10px") 231 | .style("font-family", "Arial, Helvetica") 232 | .attr("transform", "translate(" + 0 + "," + (height() - 20) + ")") 233 | .call(xAxis); 234 | 235 | d3.selectAll(".x.axis line, .x.axis path") 236 | .style("shape-rendering", "crispEdges") 237 | .style("fill", "none") 238 | .style("stroke", "#ccc") 239 | 240 | if (!showLabels()) return; 241 | 242 | g.append('defs'); 243 | 244 | g.select('defs') 245 | .selectAll('path') 246 | .data(layers) 247 | .enter().append('path') 248 | .attr('id', function(d, i) { return 'path-' + i; }) 249 | .attr('d', line); 250 | 251 | g.selectAll("text.label") 252 | .data(layers) 253 | .enter().append('text') 254 | .attr('dy', '0.5ex') 255 | .attr("class", "label") 256 | .append('textPath') 257 | .attr('xlink:xlink:href', function(d, i) { return '#path-' + i; }) 258 | .attr('startOffset', function(d) { 259 | var maxYloc = 0, 260 | maxV = 0; 261 | d.forEach(function(e, i) { 262 | // get point with max value 263 | var val = e[1] - e[0]; 264 | if (val > maxV) { 265 | maxV = val; 266 | maxYloc = i 267 | } 268 | }) 269 | //get y position 270 | d.offset = Math.round(maxYloc / d.length * 100); 271 | //return offset 272 | return Math.min(95, Math.max(5, d.offset)) + '%'; 273 | }) 274 | .attr('text-anchor', function(d) { 275 | return d.offset > 90 ? 'end' : d.offset < 10 ? 'start' : 'middle'; 276 | }) 277 | .text(function(d) { return d.key; }) 278 | .style("font-size", "11px") 279 | .style("font-family", "Arial, Helvetica") 280 | .style("font-weight", "normal") 281 | 282 | /*g.selectAll("text.label") 283 | .data(labels(layers)) 284 | .enter().append("text") 285 | .attr("x", function(d){ return x(d.x); }) 286 | .attr("y", function(d){ return y(d.y0 + d.y/2); }) 287 | .text(function(d){ return d.group; }) 288 | .style("font-size","11px") 289 | .style("font-family","Arial, Helvetica") 290 | 291 | 292 | 293 | function labels(layers){ 294 | return layers.map(function(layer){ 295 | var l = layer[0], max = 0; 296 | layer.forEach(function(d){ 297 | if ( d.y > max ) { 298 | max = d.y; 299 | l = d; 300 | } 301 | }) 302 | return l; 303 | }) 304 | 305 | }*/ 306 | 307 | }) 308 | //sorting functions 309 | function sortBy(a, b) { 310 | if (sort() == 'value (ascending)') return (a[1] - a[0]) - (b[1] - b[0]); 311 | if (sort() == 'value (descending)') return (b[1] - b[0]) - (a[1] - a[0]); 312 | } 313 | 314 | //Sankey interpolator. Could be moved in a better place. 315 | function CurveSankey(context) { 316 | this._context = context; 317 | } 318 | 319 | CurveSankey.prototype = { 320 | areaStart: function() { 321 | this._line = 0; 322 | }, 323 | areaEnd: function() { 324 | this._line = NaN; 325 | }, 326 | lineStart: function() { 327 | this._x = this._y = NaN; 328 | this._point = 0; 329 | }, 330 | lineEnd: function() { 331 | if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); 332 | this._line = 1 - this._line; 333 | }, 334 | point: function(x, y) { 335 | x = +x, y = +y; 336 | switch (this._point) { 337 | case 0: 338 | this._point = 1; 339 | this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); 340 | break; 341 | case 1: 342 | this._point = 2; // proceed 343 | default: 344 | var mx = (x - this._x) / 2 + this._x; 345 | this._context.bezierCurveTo(mx, this._y, mx, y, x, y); 346 | break; 347 | } 348 | this._x = x, this._y = y; 349 | } 350 | }; 351 | 352 | var curveSankey = function(context) { 353 | return new CurveSankey(context); 354 | } 355 | 356 | })(); 357 | -------------------------------------------------------------------------------- /charts/beeswarmPlot.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // A multiple beeswarm plot 4 | 5 | // The Model 6 | // The model abstraction is a matrix of categories: the main dimansion will define the groups, 7 | // and the secondary will define the single bars. 8 | // Optional dimension is on the bar chart color (to be defined). 9 | 10 | var model = raw.model(); 11 | 12 | // Group dimension. 13 | // It can accept both numbers and strings 14 | var groups = model.dimension() 15 | .title('Groups') 16 | .types(Number, String) 17 | 18 | // values dimension. each category will define a bar 19 | // It can accept both numbers and strings 20 | var values = model.dimension() 21 | .title('X Axis') 22 | .types(Number, Date) 23 | .required(true) 24 | 25 | // Values dimension. It will define the height of the bars 26 | var radiuses = model.dimension() 27 | .title('Radius') 28 | 29 | // Colors dimension. It will define the color of the bubbles 30 | var colorsDimesion = model.dimension() 31 | .title('Colors') 32 | .types(Number, String) 33 | 34 | // Values dimension. It will define the height of the bars 35 | var labels = model.dimension() 36 | .title('Labels') 37 | 38 | model.map(function(data) { 39 | 40 | var results = d3.nest() 41 | .key(function(d) { 42 | return d[groups()] 43 | }) 44 | .entries(data) 45 | 46 | // remap the array 47 | results.forEach(function(d) { 48 | d.values = d.values.map(function(g) { 49 | return { 50 | group: g[groups()], 51 | value: values.type() == 'Date' ? new Date(g[values()]) : +g[values()], 52 | radius: radiuses() ? +g[radiuses()] : 1, 53 | color: colorsDimesion() ? g[colorsDimesion()] : null, 54 | label: g[labels()] 55 | } 56 | }) 57 | }) 58 | 59 | return results; 60 | }) 61 | 62 | 63 | // The Chart 64 | var chart = raw.chart() 65 | .title('Beeswarm Plot') 66 | .description("It distributes elements horizontally avoiding overlap between them and according to a selected dimension.

Based on https://bl.ocks.org/mbostock/6526445e2b44303eebf21da3b6627320") 67 | .thumbnail("imgs/beeswarm.png") 68 | .category('Distribution') 69 | .model(model) 70 | 71 | // visualiziation options 72 | // Width 73 | var width = chart.number() 74 | .title('Width') 75 | .defaultValue(800) 76 | 77 | // Height 78 | var height = chart.number() 79 | .title('Height') 80 | .defaultValue(600) 81 | 82 | // Spatialization iterations 83 | var anticollisionIterations = chart.number() 84 | .title('Anticollision iterations') 85 | .defaultValue(1); 86 | 87 | var marginCircles = chart.number() 88 | .title("Circles padding") 89 | .defaultValue(.5); 90 | 91 | // Space between barcharts 92 | var padding = chart.number() 93 | .title('Vertical padding') 94 | .defaultValue(0); 95 | 96 | var minRadius = chart.number() 97 | .title("min radius") 98 | .defaultValue(2); 99 | 100 | var maxRadius = chart.number() 101 | .title("max radius") 102 | .defaultValue(20); 103 | 104 | var sorting = chart.list() 105 | .title("Sort by") 106 | .values(['Original', 'Name (ascending)', 'Name (descending)', 'Total (descending)', 'Total (ascending)']) 107 | .defaultValue('Original') 108 | 109 | // Chart colors 110 | var colors = chart.color() 111 | .title("Color scale") 112 | 113 | // Drawing function 114 | // selection represents the d3 selection (svg) 115 | // data is not the original set of records 116 | // but the result of the model map function 117 | chart.draw(function(selection, data) { 118 | 119 | //sort data 120 | function sortBy(a, b) { 121 | if (sorting() == 'Name (descending)') { 122 | if (a.key < b.key) return 1; 123 | if (a.key > b.key) return -1; 124 | return 0; 125 | } else if (sorting() == 'Name (ascending)') { 126 | if (a.key < b.key) return -1; 127 | if (a.key > b.key) return 1; 128 | return 0; 129 | } else if (sorting() == 'Total (descending)') { 130 | if (a.values.length < b.values.length) return 1; 131 | if (a.values.length > b.values.length) return -1; 132 | return 0; 133 | } else if (sorting() == 'Total (ascending)') { 134 | if (a.values.length < b.values.length) return -1; 135 | if (a.values.length > b.values.length) return 1; 136 | return 0; 137 | } 138 | } 139 | data.sort(sortBy); 140 | 141 | // Define margins 142 | var margin = { 143 | top: 50, 144 | right: 25, 145 | bottom: 0, 146 | left: 25 147 | }; 148 | 149 | //define title space 150 | var titleSpace = groups() == null ? 0 : 30; 151 | 152 | // Define common variables. 153 | 154 | // svg size 155 | selection 156 | .attr("width", width()) 157 | .attr("height", height()) 158 | 159 | // define single beswarm plot height, depending on the number of bar charts 160 | var w = +width() - margin.left - margin.right, 161 | h = (+height() - margin.bottom - margin.top - ((titleSpace + padding()) * (data.length - 1))) / data.length; 162 | 163 | // Define scales 164 | 165 | // Radiuses 166 | let rMax = d3.max(data, function(d) { 167 | return d3.max(d.values, function(e) { 168 | return e.radius; 169 | }) 170 | }) 171 | let rMin = d3.min(data, function(d) { 172 | return d3.min(d.values, function(e) { 173 | return e.radius; 174 | }) 175 | }) 176 | var radius = d3.scaleLinear() 177 | .range([minRadius(), maxRadius()]) 178 | .domain([rMin, rMax]) 179 | 180 | // colors 181 | let allColors = []; 182 | data.forEach(function(d) { 183 | d.values.forEach(function(dd) { 184 | allColors.push(dd); 185 | }) 186 | }) 187 | allColors = d3.nest() 188 | .key(function(d) { 189 | return d.color; 190 | }) 191 | .entries(allColors) 192 | .map(function(d) { 193 | return d.key 194 | }) 195 | colors.domain(allColors); 196 | 197 | // Horizontal 198 | 199 | //handle data type (Number or Date) 200 | var xScale = values.type() == 'Date' ? d3.scaleTime() : d3.scaleLinear(); 201 | 202 | let xMax = d3.max(data, function(d) { 203 | return d3.max(d.values, function(e) { 204 | return e.value; 205 | }) 206 | }) 207 | let xMin = d3.min(data, function(d) { 208 | return d3.min(d.values, function(e) { 209 | return e.value; 210 | }) 211 | }) 212 | // if (values.type() == 'Date') { 213 | // xMin = new Date(xMin); 214 | // xMax = new Date(xMax) 215 | // } 216 | 217 | xScale.range([radius(radius.domain()[0]), w - radius(radius.domain()[1])]) 218 | .domain([xMin, xMax]); 219 | 220 | // Draw each bar chart 221 | data.forEach(function(item, index) { 222 | 223 | // Append a grupo containing axis and circles, 224 | // move it according the index 225 | let beeswarm = selection.append("g") 226 | .attr('id', item.key === "undefined" ? "swarm" : "swarm-" + item.key) 227 | .attr("transform", "translate(" + margin.left + "," + index * (h + padding() + titleSpace) + ")"); 228 | 229 | // Draw title 230 | beeswarm.append("text") 231 | .attr("x", -margin.left) 232 | .attr("y", titleSpace - 7) 233 | .style("font-size", "10px") 234 | .style("font-family", "Arial, Helvetica") 235 | .text(item.key); 236 | 237 | let data = item.values; 238 | 239 | var simulation = d3.forceSimulation(data) 240 | .force("x", d3.forceX(function(d) { 241 | return xScale(d.value) 242 | }) 243 | .strength(1)) 244 | .force("y", d3.forceY(h / 2)) 245 | .force("collide", d3.forceCollide(function(d) { 246 | return radius(d.radius) + marginCircles() 247 | }).iterations(anticollisionIterations())) 248 | .stop(); 249 | 250 | for (var i = 0; i < 240; ++i) simulation.tick(); 251 | 252 | let bees = beeswarm.append('g') 253 | .attr('id', 'circles') 254 | .attr('class', 'bees') 255 | .selectAll("circle") 256 | .data(data).enter() 257 | .append('circle') 258 | .attr('id', function(d) { 259 | return d.label ? d.label : 'circle' 260 | }) 261 | .attr('r', function(d) { 262 | return radius(d.radius) 263 | }) 264 | .attr('cx', function(d) { 265 | return d.x 266 | }) 267 | .attr('cy', function(d) { 268 | return d.y 269 | }) 270 | .attr("fill", function(d) { 271 | if (d.color) { 272 | return colors()(d.color); 273 | } else { 274 | return '#444' 275 | } 276 | }); 277 | 278 | let labels = beeswarm.append('g') 279 | .attr('id', 'labels') 280 | .attr('class', 'label') 281 | .selectAll("text") 282 | .data(data).enter() 283 | .append('text') 284 | .attr('x', function(d) { 285 | return d.x 286 | }) 287 | .attr('y', function(d) { 288 | return d.y 289 | }) 290 | .attr('text-anchor', 'middle') 291 | .attr('fill', '#000') 292 | .text(function(d) { 293 | if (d.label) return d.label; 294 | }) 295 | 296 | }) 297 | 298 | // After all the charts, draw x axis 299 | selection.append("g") 300 | .attr('id', '"x-axis') 301 | .attr("class", "x axis") 302 | .style("font-size", "10px") 303 | .style("font-family", "Arial, Helvetica") 304 | .attr("transform", "translate(" + margin.left + "," + ((h + padding() + titleSpace) * data.length - padding()) + ")") 305 | .call(d3.axisBottom(xScale)); 306 | 307 | // Set styles 308 | d3.selectAll(".axis line, .axis path") 309 | .style("shape-rendering", "crispEdges") 310 | .style("fill", "none") 311 | .style("stroke", "#ccc"); 312 | 313 | }) 314 | })(); 315 | -------------------------------------------------------------------------------- /data/music.csv: -------------------------------------------------------------------------------- 1 | Media Year Market Share 2 | 8-track 1980-01 14.30 3 | 8-track 1981-01 7.90 4 | 8-track 1982-01 1.00 5 | 8-track 1983-01 0.00 6 | 8-track 1984-01 0.00 7 | 8-track 1985-01 0.00 8 | 8-track 1986-01 0.00 9 | 8-track 1987-01 0.00 10 | 8-track 1988-01 0.00 11 | 8-track 1989-01 0.00 12 | 8-track 1990-01 0.00 13 | 8-track 1991-01 0.00 14 | 8-track 1992-01 0.00 15 | 8-track 1993-01 0.00 16 | 8-track 1994-01 0.00 17 | 8-track 1995-01 0.00 18 | 8-track 1996-01 0.00 19 | 8-track 1997-01 0.00 20 | 8-track 1998-01 0.00 21 | 8-track 1999-01 0.00 22 | 8-track 2000-01 0.00 23 | 8-track 2001-01 0.00 24 | 8-track 2002-01 0.00 25 | 8-track 2003-01 0.00 26 | 8-track 2004-01 0.00 27 | 8-track 2005-01 0.00 28 | 8-track 2006-01 0.00 29 | 8-track 2007-01 0.00 30 | 8-track 2008-01 0.00 31 | 8-track 2009-01 0.00 32 | 8-track 2010-01 0.00 33 | Cassete 1980-01 19.10 34 | Cassete 1981-01 26.70 35 | Cassete 1982-01 38.20 36 | Cassete 1983-01 47.80 37 | Cassete 1984-01 55.00 38 | Cassete 1985-01 55.30 39 | Cassete 1986-01 53.90 40 | Cassete 1987-01 53.20 41 | Cassete 1988-01 54.10 42 | Cassete 1989-01 50.80 43 | Cassete 1990-01 46.00 44 | Cassete 1991-01 38.50 45 | Cassete 1992-01 34.50 46 | Cassete 1993-01 29.00 47 | Cassete 1994-01 24.70 48 | Cassete 1995-01 18.70 49 | Cassete 1996-01 15.20 50 | Cassete 1997-01 12.40 51 | Cassete 1998-01 7.30 52 | Cassete 1999-01 7.30 53 | Cassete 2000-01 4.40 54 | Cassete 2001-01 2.60 55 | Cassete 2002-01 1.70 56 | Cassete 2003-01 0.90 57 | Cassete 2004-01 0.20 58 | Cassete 2005-01 0.10 59 | Cassete 2006-01 0.00 60 | Cassete 2007-01 0.00 61 | Cassete 2008-01 0.00 62 | Cassete 2009-01 0.00 63 | Cassete 2010-01 0.00 64 | Cassete single 1980-01 0.00 65 | Cassete single 1981-01 0.00 66 | Cassete single 1982-01 0.00 67 | Cassete single 1983-01 0.00 68 | Cassete single 1984-01 0.00 69 | Cassete single 1985-01 0.00 70 | Cassete single 1986-01 0.00 71 | Cassete single 1987-01 0.30 72 | Cassete single 1988-01 0.90 73 | Cassete single 1989-01 3.00 74 | Cassete single 1990-01 3.40 75 | Cassete single 1991-01 2.90 76 | Cassete single 1992-01 3.30 77 | Cassete single 1993-01 3.00 78 | Cassete single 1994-01 2.30 79 | Cassete single 1995-01 1.90 80 | Cassete single 1996-01 1.50 81 | Cassete single 1997-01 1.10 82 | Cassete single 1998-01 0.30 83 | Cassete single 1999-01 0.30 84 | Cassete single 2000-01 0.00 85 | Cassete single 2001-01 0.00 86 | Cassete single 2002-01 0.00 87 | Cassete single 2003-01 0.00 88 | Cassete single 2004-01 0.00 89 | Cassete single 2005-01 0.00 90 | Cassete single 2006-01 0.00 91 | Cassete single 2007-01 0.00 92 | Cassete single 2008-01 0.00 93 | Cassete single 2009-01 0.00 94 | Cassete single 2010-01 0.00 95 | CD 1980-01 0.00 96 | CD 1981-01 0.00 97 | CD 1982-01 0.00 98 | CD 1983-01 0.50 99 | CD 1984-01 2.40 100 | CD 1985-01 8.90 101 | CD 1986-01 20.00 102 | CD 1987-01 28.50 103 | CD 1988-01 33.40 104 | CD 1989-01 39.30 105 | CD 1990-01 45.80 106 | CD 1991-01 55.50 107 | CD 1992-01 59.20 108 | CD 1993-01 64.80 109 | CD 1994-01 70.10 110 | CD 1995-01 76.10 111 | CD 1996-01 79.20 112 | CD 1997-01 81.10 113 | CD 1998-01 87.90 114 | CD 1999-01 87.90 115 | CD 2000-01 92.30 116 | CD 2001-01 94.00 117 | CD 2002-01 95.30 118 | CD 2003-01 94.70 119 | CD 2004-01 92.70 120 | CD 2005-01 85.60 121 | CD 2006-01 79.70 122 | CD 2007-01 70.00 123 | CD 2008-01 62.40 124 | CD 2009-01 55.60 125 | CD 2010-01 49.10 126 | CD single 1980-01 0.00 127 | CD single 1981-01 0.00 128 | CD single 1982-01 0.00 129 | CD single 1983-01 0.00 130 | CD single 1984-01 0.00 131 | CD single 1985-01 0.00 132 | CD single 1986-01 0.00 133 | CD single 1987-01 0.00 134 | CD single 1988-01 0.20 135 | CD single 1989-01 0.00 136 | CD single 1990-01 0.10 137 | CD single 1991-01 0.40 138 | CD single 1992-01 0.50 139 | CD single 1993-01 0.50 140 | CD single 1994-01 0.50 141 | CD single 1995-01 0.90 142 | CD single 1996-01 1.50 143 | CD single 1997-01 2.20 144 | CD single 1998-01 1.50 145 | CD single 1999-01 1.50 146 | CD single 2000-01 1.00 147 | CD single 2001-01 0.60 148 | CD single 2002-01 0.20 149 | CD single 2003-01 0.30 150 | CD single 2004-01 0.10 151 | CD single 2005-01 0.10 152 | CD single 2006-01 0.10 153 | CD single 2007-01 0.10 154 | CD single 2008-01 0.00 155 | CD single 2009-01 0.00 156 | CD single 2010-01 0.00 157 | Download Album 1980-01 0.00 158 | Download Album 1981-01 0.00 159 | Download Album 1982-01 0.00 160 | Download Album 1983-01 0.00 161 | Download Album 1984-01 0.00 162 | Download Album 1985-01 0.00 163 | Download Album 1986-01 0.00 164 | Download Album 1987-01 0.00 165 | Download Album 1988-01 0.00 166 | Download Album 1989-01 0.00 167 | Download Album 1990-01 0.00 168 | Download Album 1991-01 0.00 169 | Download Album 1992-01 0.00 170 | Download Album 1993-01 0.00 171 | Download Album 1994-01 0.00 172 | Download Album 1995-01 0.00 173 | Download Album 1996-01 0.00 174 | Download Album 1997-01 0.00 175 | Download Album 1998-01 0.00 176 | Download Album 1999-01 0.00 177 | Download Album 2000-01 0.00 178 | Download Album 2001-01 0.00 179 | Download Album 2002-01 0.00 180 | Download Album 2003-01 0.00 181 | Download Album 2004-01 0.40 182 | Download Album 2005-01 1.20 183 | Download Album 2006-01 2.50 184 | Download Album 2007-01 4.90 185 | Download Album 2008-01 7.90 186 | Download Album 2009-01 11.00 187 | Download Album 2010-01 14.80 188 | Download Music Video 1980-01 0.00 189 | Download Music Video 1981-01 0.00 190 | Download Music Video 1982-01 0.00 191 | Download Music Video 1983-01 0.00 192 | Download Music Video 1984-01 0.00 193 | Download Music Video 1985-01 0.00 194 | Download Music Video 1986-01 0.00 195 | Download Music Video 1987-01 0.00 196 | Download Music Video 1988-01 0.00 197 | Download Music Video 1989-01 0.00 198 | Download Music Video 1990-01 0.00 199 | Download Music Video 1991-01 0.00 200 | Download Music Video 1992-01 0.00 201 | Download Music Video 1993-01 0.00 202 | Download Music Video 1994-01 0.00 203 | Download Music Video 1995-01 0.00 204 | Download Music Video 1996-01 0.00 205 | Download Music Video 1997-01 0.00 206 | Download Music Video 1998-01 0.00 207 | Download Music Video 1999-01 0.00 208 | Download Music Video 2000-01 0.00 209 | Download Music Video 2001-01 0.00 210 | Download Music Video 2002-01 0.00 211 | Download Music Video 2003-01 0.00 212 | Download Music Video 2004-01 0.00 213 | Download Music Video 2005-01 0.00 214 | Download Music Video 2006-01 0.20 215 | Download Music Video 2007-01 0.30 216 | Download Music Video 2008-01 0.50 217 | Download Music Video 2009-01 0.50 218 | Download Music Video 2010-01 0.50 219 | Download Single 1980-01 0.00 220 | Download Single 1981-01 0.00 221 | Download Single 1982-01 0.00 222 | Download Single 1983-01 0.00 223 | Download Single 1984-01 0.00 224 | Download Single 1985-01 0.00 225 | Download Single 1986-01 0.00 226 | Download Single 1987-01 0.00 227 | Download Single 1988-01 0.00 228 | Download Single 1989-01 0.00 229 | Download Single 1990-01 0.00 230 | Download Single 1991-01 0.00 231 | Download Single 1992-01 0.00 232 | Download Single 1993-01 0.00 233 | Download Single 1994-01 0.00 234 | Download Single 1995-01 0.00 235 | Download Single 1996-01 0.00 236 | Download Single 1997-01 0.00 237 | Download Single 1998-01 0.00 238 | Download Single 1999-01 0.00 239 | Download Single 2000-01 0.00 240 | Download Single 2001-01 0.00 241 | Download Single 2002-01 0.00 242 | Download Single 2003-01 0.00 243 | Download Single 2004-01 1.10 244 | Download Single 2005-01 3.10 245 | Download Single 2006-01 5.10 246 | Download Single 2007-01 7.80 247 | Download Single 2008-01 12.50 248 | Download Single 2009-01 16.90 249 | Download Single 2010-01 21.00 250 | DVD Audio 1980-01 0.00 251 | DVD Audio 1981-01 0.00 252 | DVD Audio 1982-01 0.00 253 | DVD Audio 1983-01 0.00 254 | DVD Audio 1984-01 0.00 255 | DVD Audio 1985-01 0.00 256 | DVD Audio 1986-01 0.00 257 | DVD Audio 1987-01 0.00 258 | DVD Audio 1988-01 0.00 259 | DVD Audio 1989-01 0.00 260 | DVD Audio 1990-01 0.00 261 | DVD Audio 1991-01 0.00 262 | DVD Audio 1992-01 0.00 263 | DVD Audio 1993-01 0.00 264 | DVD Audio 1994-01 0.00 265 | DVD Audio 1995-01 0.00 266 | DVD Audio 1996-01 0.00 267 | DVD Audio 1997-01 0.00 268 | DVD Audio 1998-01 0.00 269 | DVD Audio 1999-01 0.00 270 | DVD Audio 2000-01 0.00 271 | DVD Audio 2001-01 0.00 272 | DVD Audio 2002-01 0.10 273 | DVD Audio 2003-01 0.10 274 | DVD Audio 2004-01 0.10 275 | DVD Audio 2005-01 0.10 276 | DVD Audio 2006-01 0.00 277 | DVD Audio 2007-01 0.00 278 | DVD Audio 2008-01 0.00 279 | DVD Audio 2009-01 0.00 280 | DVD Audio 2010-01 0.00 281 | Kiosk 1980-01 0.00 282 | Kiosk 1981-01 0.00 283 | Kiosk 1982-01 0.00 284 | Kiosk 1983-01 0.00 285 | Kiosk 1984-01 0.00 286 | Kiosk 1985-01 0.00 287 | Kiosk 1986-01 0.00 288 | Kiosk 1987-01 0.00 289 | Kiosk 1988-01 0.00 290 | Kiosk 1989-01 0.00 291 | Kiosk 1990-01 0.00 292 | Kiosk 1991-01 0.00 293 | Kiosk 1992-01 0.00 294 | Kiosk 1993-01 0.00 295 | Kiosk 1994-01 0.00 296 | Kiosk 1995-01 0.00 297 | Kiosk 1996-01 0.00 298 | Kiosk 1997-01 0.00 299 | Kiosk 1998-01 0.00 300 | Kiosk 1999-01 0.00 301 | Kiosk 2000-01 0.00 302 | Kiosk 2001-01 0.00 303 | Kiosk 2002-01 0.00 304 | Kiosk 2003-01 0.00 305 | Kiosk 2004-01 0.00 306 | Kiosk 2005-01 0.00 307 | Kiosk 2006-01 0.00 308 | Kiosk 2007-01 0.00 309 | Kiosk 2008-01 0.00 310 | Kiosk 2009-01 0.10 311 | Kiosk 2010-01 0.10 312 | LP/EP 1980-01 59.80 313 | LP/EP 1981-01 58.90 314 | LP/EP 1982-01 53.10 315 | LP/EP 1983-01 44.60 316 | LP/EP 1984-01 35.70 317 | LP/EP 1985-01 29.40 318 | LP/EP 1986-01 21.20 319 | LP/EP 1987-01 14.30 320 | LP/EP 1988-01 8.50 321 | LP/EP 1989-01 3.30 322 | LP/EP 1990-01 1.10 323 | LP/EP 1991-01 0.40 324 | LP/EP 1992-01 0.10 325 | LP/EP 1993-01 0.10 326 | LP/EP 1994-01 0.10 327 | LP/EP 1995-01 0.20 328 | LP/EP 1996-01 0.30 329 | LP/EP 1997-01 0.30 330 | LP/EP 1998-01 0.20 331 | LP/EP 1999-01 0.20 332 | LP/EP 2000-01 0.20 333 | LP/EP 2001-01 0.20 334 | LP/EP 2002-01 0.20 335 | LP/EP 2003-01 0.20 336 | LP/EP 2004-01 0.20 337 | LP/EP 2005-01 0.10 338 | LP/EP 2006-01 0.10 339 | LP/EP 2007-01 0.20 340 | LP/EP 2008-01 0.60 341 | LP/EP 2009-01 0.80 342 | LP/EP 2010-01 1.30 343 | Mobile 1980-01 0.00 344 | Mobile 1981-01 0.00 345 | Mobile 1982-01 0.00 346 | Mobile 1983-01 0.00 347 | Mobile 1984-01 0.00 348 | Mobile 1985-01 0.00 349 | Mobile 1986-01 0.00 350 | Mobile 1987-01 0.00 351 | Mobile 1988-01 0.00 352 | Mobile 1989-01 0.00 353 | Mobile 1990-01 0.00 354 | Mobile 1991-01 0.00 355 | Mobile 1992-01 0.00 356 | Mobile 1993-01 0.00 357 | Mobile 1994-01 0.00 358 | Mobile 1995-01 0.00 359 | Mobile 1996-01 0.00 360 | Mobile 1997-01 0.00 361 | Mobile 1998-01 0.00 362 | Mobile 1999-01 0.00 363 | Mobile 2000-01 0.00 364 | Mobile 2001-01 0.00 365 | Mobile 2002-01 0.00 366 | Mobile 2003-01 0.00 367 | Mobile 2004-01 0.00 368 | Mobile 2005-01 3.40 369 | Mobile 2006-01 6.60 370 | Mobile 2007-01 9.90 371 | Mobile 2008-01 11.10 372 | Mobile 2009-01 9.50 373 | Mobile 2010-01 7.70 374 | Music video 1980-01 0.00 375 | Music video 1981-01 0.00 376 | Music video 1982-01 0.00 377 | Music video 1983-01 0.00 378 | Music video 1984-01 0.00 379 | Music video 1985-01 0.00 380 | Music video 1986-01 0.00 381 | Music video 1987-01 0.00 382 | Music video 1988-01 0.00 383 | Music video 1989-01 1.80 384 | Music video 1990-01 2.30 385 | Music video 1991-01 1.50 386 | Music video 1992-01 1.70 387 | Music video 1993-01 2.10 388 | Music video 1994-01 1.90 389 | Music video 1995-01 1.80 390 | Music video 1996-01 1.90 391 | Music video 1997-01 2.60 392 | Music video 1998-01 2.60 393 | Music video 1999-01 2.60 394 | Music video 2000-01 2.00 395 | Music video 2001-01 2.40 396 | Music video 2002-01 2.30 397 | Music video 2003-01 3.40 398 | Music video 2004-01 4.90 399 | Music video 2005-01 4.90 400 | Music video 2006-01 3.80 401 | Music video 2007-01 4.60 402 | Music video 2008-01 2.50 403 | Music video 2009-01 2.80 404 | Music video 2010-01 2.60 405 | SACD 1980-01 0.00 406 | SACD 1981-01 0.00 407 | SACD 1982-01 0.00 408 | SACD 1983-01 0.00 409 | SACD 1984-01 0.00 410 | SACD 1985-01 0.00 411 | SACD 1986-01 0.00 412 | SACD 1987-01 0.00 413 | SACD 1988-01 0.00 414 | SACD 1989-01 0.00 415 | SACD 1990-01 0.00 416 | SACD 1991-01 0.00 417 | SACD 1992-01 0.00 418 | SACD 1993-01 0.00 419 | SACD 1994-01 0.00 420 | SACD 1995-01 0.00 421 | SACD 1996-01 0.00 422 | SACD 1997-01 0.00 423 | SACD 1998-01 0.00 424 | SACD 1999-01 0.00 425 | SACD 2000-01 0.00 426 | SACD 2001-01 0.00 427 | SACD 2002-01 0.00 428 | SACD 2003-01 0.20 429 | SACD 2004-01 0.10 430 | SACD 2005-01 0.10 431 | SACD 2006-01 0.00 432 | SACD 2007-01 0.00 433 | SACD 2008-01 0.00 434 | SACD 2009-01 0.00 435 | SACD 2010-01 0.00 436 | Subscription 1980-01 0.00 437 | Subscription 1981-01 0.00 438 | Subscription 1982-01 0.00 439 | Subscription 1983-01 0.00 440 | Subscription 1984-01 0.00 441 | Subscription 1985-01 0.00 442 | Subscription 1986-01 0.00 443 | Subscription 1987-01 0.00 444 | Subscription 1988-01 0.00 445 | Subscription 1989-01 0.00 446 | Subscription 1990-01 0.00 447 | Subscription 1991-01 0.00 448 | Subscription 1992-01 0.00 449 | Subscription 1993-01 0.00 450 | Subscription 1994-01 0.00 451 | Subscription 1995-01 0.00 452 | Subscription 1996-01 0.00 453 | Subscription 1997-01 0.00 454 | Subscription 1998-01 0.00 455 | Subscription 1999-01 0.00 456 | Subscription 2000-01 0.00 457 | Subscription 2001-01 0.00 458 | Subscription 2002-01 0.00 459 | Subscription 2003-01 0.00 460 | Subscription 2004-01 0.00 461 | Subscription 2005-01 1.20 462 | Subscription 2006-01 1.80 463 | Subscription 2007-01 2.20 464 | Subscription 2008-01 2.50 465 | Subscription 2009-01 2.80 466 | Subscription 2010-01 2.90 467 | vinyl single 1980-01 6.80 468 | vinyl single 1981-01 6.50 469 | vinyl single 1982-01 7.70 470 | vinyl single 1983-01 7.10 471 | vinyl single 1984-01 6.90 472 | vinyl single 1985-01 6.40 473 | vinyl single 1986-01 4.90 474 | vinyl single 1987-01 3.70 475 | vinyl single 1988-01 2.90 476 | vinyl single 1989-01 1.80 477 | vinyl single 1990-01 1.30 478 | vinyl single 1991-01 0.80 479 | vinyl single 1992-01 0.70 480 | vinyl single 1993-01 0.50 481 | vinyl single 1994-01 0.40 482 | vinyl single 1995-01 0.40 483 | vinyl single 1996-01 0.40 484 | vinyl single 1997-01 0.30 485 | vinyl single 1998-01 0.20 486 | vinyl single 1999-01 0.20 487 | vinyl single 2000-01 0.20 488 | vinyl single 2001-01 0.20 489 | vinyl single 2002-01 0.20 490 | vinyl single 2003-01 0.20 491 | vinyl single 2004-01 0.20 492 | vinyl single 2005-01 0.10 493 | vinyl single 2006-01 0.10 494 | vinyl single 2007-01 0.00 495 | vinyl single 2008-01 0.00 496 | vinyl single 2009-01 0.00 497 | vinyl single 2010-01 0.00 -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RAW 5 | 6 | 7 | 11 | 15 | 19 | 20 | 21 | 22 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 46 | 47 | 51 | 56 | 60 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 147 | 148 |
149 | 150 | 206 | 207 | 208 | 209 | 235 | 236 | 237 | 241 | 245 | 249 | 250 | 254 | 258 | 259 | 260 | 261 | 265 | 269 | 273 | 277 | 281 | 282 | 286 | 290 | 291 | 295 | 296 | 300 | 301 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 336 | 340 | 344 | 348 | 352 | 356 | 360 | 364 | 368 | 372 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 392 | 393 | 394 | --------------------------------------------------------------------------------