├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── README.md ├── docs ├── .vitepress │ └── config.js └── index.md ├── examples ├── annotated-visualizations │ ├── d3 │ │ ├── annotated-bar-chart.js │ │ └── annotated-scatter-plot.js │ ├── ggplot2 │ │ └── annotated-line-chart.js │ ├── multi-view │ │ └── annotated-vega-lite-weather.js │ └── observable-plot │ │ └── annotated-scatter-plot.js ├── data │ ├── XYZ.csv │ ├── alphabet.csv │ ├── cities-lived.csv │ ├── data.csv │ ├── flights-2k.json │ ├── penguins.csv │ ├── seattle-weather.csv │ ├── stateslived.csv │ ├── us-states.json │ └── vega-lite-schema-v5.json └── visualizations │ ├── d3 │ ├── bar-chart.js │ ├── custom-map.js │ ├── glyph-map.js │ ├── hex-map.js │ ├── line-chart.js │ ├── log-chart.js │ ├── pie-chart.js │ ├── scatter-plot.js │ ├── stacked-area-chart.js │ └── stacked-bar-chart.js │ ├── ggplot2 │ ├── line-chart.svg │ ├── multi-view-setup │ │ ├── excel-scatter.png │ │ ├── excel-scatter.svg │ │ ├── line.svg │ │ ├── multi-view-setup.R │ │ ├── scatter.svg │ │ ├── seattle-weather.csv │ │ └── seattle-weather.png │ ├── r4-1.svg │ ├── r4-2.svg │ ├── scatter-line-chart.svg │ ├── stock-multiline.R │ ├── stock-multiline.svg │ ├── weather-scatter.svg │ ├── weather-scatter2.svg │ ├── weather-scatter3.svg │ └── weather-scatter4.svg │ ├── matplotlib │ ├── bar-chart.py │ ├── bars.svg │ ├── bars1.svg │ ├── bars2.svg │ ├── multi-view-setup │ │ ├── bar-charts.py │ │ ├── bars1.svg │ │ ├── bars2.svg │ │ └── bars_test.svg │ └── multi-view.py │ ├── multi-view │ ├── d3-ggplot2.js │ ├── gg-matplot.js │ ├── gg-weather-facet.js │ ├── ggplot2-setup.js │ ├── ggplot2-vegalite.js │ ├── vega-bar-scatter.js │ ├── vega-crossfilter.js │ ├── vega-dual-linking.js │ ├── vega-lite-weather.js │ └── vega-matplotlib.js │ ├── observable-plot │ ├── bar-chart.js │ ├── facets.js │ └── scatter-plot.js │ └── vega-lite │ ├── bar-chart.js │ ├── line-chart.js │ ├── scatter-plot-1.js │ └── scatter-plot-2.js ├── package-lock.json ├── package.json ├── public ├── index.html ├── pages │ ├── d3 │ │ ├── bar-chart.html │ │ ├── hex-map.html │ │ ├── line-chart.html │ │ ├── log-chart.html │ │ ├── population-map.html │ │ ├── scatter-plot.html │ │ ├── stacked-area-chart.html │ │ └── stacked-bar-chart.html │ ├── ggplot2 │ │ ├── line-chart.html │ │ └── trendline.html │ ├── multi-view │ │ ├── d3-ggplot2.html │ │ ├── gg-matplot.html │ │ ├── gg-weather-facet.html │ │ ├── ggplot2-multi-view-setup.html │ │ ├── vega-bar-scatter.html │ │ ├── vega-crossfilter.html │ │ ├── vega-dual-linking.html │ │ ├── vega-ggplot2.html │ │ ├── vega-lite-weather.html │ │ └── vega-matplotlib.html │ ├── observable-plot │ │ ├── bar-chart.html │ │ ├── facets.html │ │ └── scatter-plot.html │ └── vega-lite │ │ ├── bar-chart.html │ │ ├── scatter-plot-1.html │ │ └── scatter-plot-2.html └── study.html ├── rollup.config.js ├── src ├── _d3 │ ├── axis.js │ ├── identity.js │ └── zoom │ │ ├── constant.js │ │ ├── event.js │ │ ├── index.js │ │ ├── noevent.js │ │ ├── transform.js │ │ └── zoom.js ├── handlers │ ├── annotate.js │ ├── brush.js │ ├── query.js │ ├── select.js │ ├── sort.js │ └── zoom.js ├── hydrate.js ├── index.js ├── orchestration │ ├── coordinator.js │ └── inspect.js ├── parsers │ ├── engine │ │ ├── parser-engine.js │ │ └── parser-groups.js │ ├── helpers │ │ ├── axis-parser.js │ │ ├── data-parser.js │ │ ├── legend-parser.js │ │ ├── mark-parser.js │ │ └── title-parser.js │ └── multi-view │ │ └── link-parser.js ├── state │ ├── constants.js │ ├── data-state.js │ └── view-state.js ├── toolbar │ ├── icons │ │ ├── annotate.svg │ │ ├── brush.svg │ │ ├── download.svg │ │ ├── filter.svg │ │ ├── link.svg │ │ ├── navigate.svg │ │ └── reset.svg │ └── menu.js └── util │ ├── transform.js │ └── util.js └── test ├── browser ├── d3 │ ├── bar-chart-test.js │ ├── scatter-plot-test.js │ └── tests.js ├── excel │ └── tests.js ├── ggplot2 │ ├── line-chart-test.js │ └── tests.js ├── index.html ├── matplotlib │ └── tests.js ├── multi-view │ ├── tests.js │ └── vega-lite-weather-test.js ├── observable-plot │ ├── scatter-plot-test.js │ └── tests.js └── vega-lite │ └── tests.js ├── node └── util-test.js └── util ├── core-structure-test-functions.js ├── helper-functions.js └── test-constants.js /.eslintignore: -------------------------------------------------------------------------------- 1 | /src/_d3/ 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "es6": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | "extends": "standard", 9 | "overrides": [ 10 | ], 11 | "parserOptions": { 12 | "ecmaVersion": "latest", 13 | "sourceType": "module" 14 | }, 15 | "rules": { 16 | "indent": ["error", 4], 17 | "semi": ["error", "always"], 18 | "space-before-function-paren": ["error", "never"] 19 | }, 20 | "globals": { 21 | "chai": "readonly" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | concurrency: 15 | group: "pages" 16 | cancel-in-progress: false 17 | 18 | jobs: 19 | deploy: 20 | runs-on: ubuntu-latest 21 | 22 | environment: 23 | name: github-pages 24 | url: ${{ steps.deployment.outputs.page_url }} 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | with: 29 | fetch-depth: 0 30 | 31 | - uses: actions/setup-node@v4 32 | with: 33 | node-version: "20.x" 34 | cache: "npm" 35 | 36 | - name: Install Node dependencies 37 | run: npm ci 38 | 39 | - name: Build 40 | run: npm run docs:build 41 | 42 | - uses: actions/configure-pages@v5 43 | - uses: actions/upload-pages-artifact@v3 44 | with: 45 | path: docs/.vitepress/dist 46 | - name: Deploy 47 | id: deployment 48 | uses: actions/deploy-pages@v4 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules 3 | /dist 4 | docs/.vitepress/cache 5 | docs/.vitepress/dist 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DIVI 2 | 3 | DIVI: Dynamically Interactive Visualization. 4 | 5 | This repository is in the process of being updated. 6 | 7 | Please email snyderl AT cs DOT washington DOT edu to be notified when the update is complete. 8 | 9 | ## Instructions 10 | 11 | - Run `npm install` to install dependencies. 12 | - Run `npm run serve` to launch development server and view test examples. 13 | - Run `npm run build` to produce an output bundle. 14 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | text: | 7 | Dynamically Interactive Visualization 8 | tagline: | 9 | DIVI automatically orchestrates interactions within and across SVG visualizations. 10 | NOTE: This page is being actively updated. 11 | actions: 12 | - theme: brand 13 | text: What is DIVI? 14 | link: /what-is-mosaic/ 15 | - theme: alt 16 | text: Get started 17 | link: /get-started/ 18 | - theme: alt 19 | text: Examples 20 | link: /examples/ 21 | - theme: alt 22 | text: GitHub 23 | link: 'https://github.com/uwdata/divi' 24 | 25 | features: 26 | - icon: 📊 27 | title: Interact automatically 28 | details: Explore charts on-the-fly without writing complex interaction handling code. 29 | - icon: 🔗 30 | title: Perform linked interactions 31 | details: Analyze data via linked selection & brushing, navigation, filtering, annotation, and sorting. 32 | - icon: 🛠️ 33 | title: Interoperable across tools 34 | details: Interact with SVG charts from popular tools, including Matplotlib, ggplot2, and Excel. 35 | - icon: 🪡 36 | title: Customize chart 37 | details: Access deconstruction metadata such as axes and legends to tailor plotting behavior. 38 | --- 39 | -------------------------------------------------------------------------------- /examples/annotated-visualizations/d3/annotated-bar-chart.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | import { TEST_MARK, TEST_X_AXIS_LABEL, TEST_X_AXIS_TICK, TEST_Y_AXIS_LABEL, TEST_Y_AXIS_TICK } from '../../../test/util/test-constants.js'; 3 | 4 | export async function createBarChart() { 5 | // set the dimensions and margins of the graph 6 | const margin = { top: 30, right: 30, bottom: 70, left: 60 }; 7 | const width = 720 - margin.left - margin.right; 8 | const height = 720 - margin.top - margin.bottom; 9 | 10 | // append the svg object to the body of the page 11 | let svg = d3.create('svg') 12 | .attr('width', width + margin.left + margin.right) 13 | .attr('height', height + margin.top + margin.bottom) 14 | .attr('id', 'chart'); 15 | 16 | const r = svg; 17 | svg = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 18 | 19 | // Parse the Data 20 | const data = await d3.csv('https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/7_OneCatOneNum_header.csv'); 21 | 22 | // sort data 23 | data.sort(function(b, a) { 24 | return a.Value - b.Value; 25 | }); 26 | 27 | // X axis 28 | const x = d3.scaleBand() 29 | .range([0, width]) 30 | .domain(data.map(function(d) { return d.Country; })) 31 | .padding(0.2); 32 | 33 | const xAxis = svg.append('g') 34 | .attr('transform', 'translate(0,' + height + ')') 35 | .call(d3.axisBottom(x)); 36 | xAxis.selectAll('text') 37 | .attr('transform', 'translate(-10,0)rotate(-45)') 38 | .style('text-anchor', 'end'); 39 | 40 | // x-axis annotations 41 | xAxis.selectAll('text').classed(TEST_X_AXIS_LABEL, true); 42 | xAxis.selectAll('line').classed(TEST_X_AXIS_TICK, true); 43 | 44 | // Add Y axis 45 | const y = d3.scaleLinear() 46 | .domain([0, 13000]) 47 | .range([height, 0]); 48 | const yAxis = svg.append('g') 49 | .call(d3.axisLeft(y)); 50 | 51 | // y-axis annotations 52 | yAxis.selectAll('text').classed(TEST_Y_AXIS_LABEL, true); 53 | yAxis.selectAll('line').classed(TEST_Y_AXIS_TICK, true); 54 | 55 | // Bars 56 | svg.selectAll('mybar') 57 | .data(data) 58 | .enter() 59 | .append('rect') 60 | .attr('x', function(d) { return x(d.Country); }) 61 | .attr('y', function(d) { return y(d.Value); }) 62 | .attr('width', x.bandwidth()) 63 | .attr('height', function(d) { return height - y(d.Value); }) 64 | .attr('fill', '#69b3a2') 65 | .classed(TEST_MARK, true); // Mark annotations 66 | 67 | return r.node(); 68 | } 69 | -------------------------------------------------------------------------------- /examples/annotated-visualizations/d3/annotated-scatter-plot.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | import { TEST_MARK, TEST_X_AXIS_LABEL, TEST_X_AXIS_TICK, TEST_X_AXIS_TITLE, TEST_Y_AXIS_LABEL, TEST_Y_AXIS_TICK, TEST_Y_AXIS_TITLE } from '../../../test/util/test-constants.js'; 3 | 4 | export async function createScatterPlot() { 5 | // set the dimensions and margins of the graph 6 | const margin = { top: 10, right: 30, bottom: 40, left: 50 }; 7 | const width = 720 - margin.left - margin.right; 8 | const height = 720 - margin.top - margin.bottom; 9 | 10 | // append the svg object to the body of the page 11 | let svg = d3.create('svg') 12 | .attr('width', width + margin.left + margin.right) 13 | .attr('height', height + margin.top + margin.bottom) 14 | .attr('id', 'chart'); 15 | 16 | const r = svg; 17 | svg = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 18 | 19 | // Read the data 20 | const data = await d3.csv('https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/iris.csv'); 21 | 22 | // Add X axis 23 | const x = d3.scaleLinear() 24 | .domain([4 * 0.95, 8 * 1.001]) 25 | .range([0, width]); 26 | 27 | const xAxis = svg.append('g') 28 | .attr('transform', 'translate(0,' + height + ')') 29 | .call(d3.axisBottom(x).tickSize(-height * 1.3).ticks(10)); 30 | 31 | // x-axis annotations 32 | xAxis.selectAll('text').classed(TEST_X_AXIS_LABEL, true); 33 | xAxis.selectAll('line').classed(TEST_X_AXIS_TICK, true); 34 | xAxis.select('.domain').remove(); 35 | 36 | // Add Y axis 37 | const y = d3.scaleLinear() 38 | .domain([-0.001, 9 * 1.01]) 39 | .range([height, 0]) 40 | .nice(); 41 | 42 | const yAxis = svg.append('g') 43 | .call(d3.axisLeft(y).tickSize(-width * 1.3).ticks(7)); 44 | 45 | // y-axis annotations 46 | yAxis.selectAll('text').classed(TEST_Y_AXIS_LABEL, true); 47 | yAxis.selectAll('line').classed(TEST_Y_AXIS_TICK, true); 48 | yAxis.select('.domain').remove(); 49 | 50 | // Customization 51 | svg.selectAll('.tick line').attr('stroke', 'black').attr('opacity', 0.3); 52 | 53 | // Add X axis label: 54 | svg.append('text') 55 | .attr('text-anchor', 'end') 56 | .attr('x', width / 2 + margin.left) 57 | .attr('y', height + margin.top + 20) 58 | .text('Sepal Length') 59 | .classed(TEST_X_AXIS_TITLE, true); // x-axis title annotation 60 | 61 | // Y axis label: 62 | svg.append('text') 63 | .attr('text-anchor', 'end') 64 | .attr('transform', 'rotate(-90)') 65 | .attr('y', -margin.left + 20) 66 | .attr('x', -margin.top - height / 2 + 20) 67 | .text('Petal Length') 68 | .classed(TEST_Y_AXIS_TITLE, true); // y-axis title annotation 69 | 70 | // Color scale: give me a specie name, I return a color 71 | const color = d3.scaleOrdinal() 72 | .domain(['setosa', 'versicolor', 'virginica']) 73 | .range(['#F8766D', '#00BA38', '#619CFF']); 74 | 75 | // Add dots 76 | svg.append('g') 77 | .selectAll('dot') 78 | .data(data) 79 | .enter() 80 | .append('circle') 81 | .attr('cx', function(d) { return x(d.Sepal_Length); }) 82 | .attr('cy', function(d) { return y(d.Petal_Length); }) 83 | .attr('r', 5) 84 | .style('fill', function(d) { return color(d.Species); }) 85 | .classed(TEST_MARK, true); // Mark annotations 86 | 87 | return r.node(); 88 | } 89 | -------------------------------------------------------------------------------- /examples/annotated-visualizations/ggplot2/annotated-line-chart.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | 3 | import { 4 | TEST_LEGEND_LABEL, TEST_LEGEND_MARK, TEST_LEGEND_TITLE, TEST_MARK, 5 | TEST_X_AXIS_LABEL, TEST_X_AXIS_TICK, TEST_X_AXIS_TITLE, TEST_Y_AXIS_LABEL, 6 | TEST_Y_AXIS_TICK, TEST_Y_AXIS_TITLE 7 | } from '../../../test/util/test-constants.js'; 8 | 9 | const baseID = 'svg_c9f6953f-273d-40cc-a962-d74ae6872c72_'; 10 | 11 | const xAxisTickIDs = [ 12 | 'el_7', 'el_8', 'el_9', 'el_16', 'el_17' 13 | ]; 14 | 15 | const xAxisLabelIDs = [ 16 | 'el_34', 'el_35' 17 | ]; 18 | 19 | const yAxisTickIDs = [ 20 | 'el_2', 'el_3', 'el_4', 'el_5', 'el_6', 'el_10', 21 | 'el_11', 'el_12', 'el_13', 'el_14', 'el_15' 22 | ]; 23 | 24 | const yAxisLabelIDs = [ 25 | 'el_28', 'el_29', 'el_30', 'el_31', 'el_32', 'el_33' 26 | ]; 27 | 28 | const markIDs = [ 29 | 'el_18', 'el_19', 'el_20', 'el_21', 'el_22', 'el_23', 30 | 'el_24', 'el_25', 'el_26', 'el_27' 31 | ]; 32 | 33 | const legendMarkIDs = [ 34 | 'el_39', 'el_40', 'el_41', 'el_42', 'el_43', 'el_44', 35 | 'el_45', 'el_46', 'el_47', 'el_48' 36 | ]; 37 | 38 | const legendLabelIDs = [ 39 | 'el_49', 'el_50', 'el_51', 'el_52', 'el_53', 'el_54', 40 | 'el_55', 'el_56', 'el_57', 'el_58' 41 | ]; 42 | 43 | const legendTitleID = 'el_38'; 44 | const xAxisTitleID = 'el_36'; 45 | const yAxisTitleID = 'el_37'; 46 | 47 | export async function createLineChart() { 48 | const response = await fetch('/examples/visualizations/ggplot2/line-chart.svg'); 49 | const text = await response.text(); 50 | 51 | // Add annotations 52 | const svg = d3.create('div').html(text).select('svg'); 53 | 54 | // Marks 55 | for (const id of markIDs) { 56 | svg.select('#' + baseID + id).classed(TEST_MARK, true); 57 | } 58 | 59 | // x-axis ticks 60 | for (const id of xAxisTickIDs) { 61 | svg.select('#' + baseID + id).classed(TEST_X_AXIS_TICK, true); 62 | } 63 | 64 | // x-axis labels 65 | for (const id of xAxisLabelIDs) { 66 | svg.select('#' + baseID + id).classed(TEST_X_AXIS_LABEL, true); 67 | } 68 | 69 | // x-axis title 70 | svg.select('#' + baseID + xAxisTitleID).classed(TEST_X_AXIS_TITLE, true); 71 | 72 | // y-axis ticks 73 | for (const id of yAxisTickIDs) { 74 | svg.select('#' + baseID + id).classed(TEST_Y_AXIS_TICK, true); 75 | } 76 | 77 | // y-axis labels 78 | for (const id of yAxisLabelIDs) { 79 | svg.select('#' + baseID + id).classed(TEST_Y_AXIS_LABEL, true); 80 | } 81 | 82 | // y-axis title 83 | svg.select('#' + baseID + yAxisTitleID).classed(TEST_Y_AXIS_TITLE, true); 84 | 85 | // Legend marks 86 | for (const id of legendMarkIDs) { 87 | svg.select('#' + baseID + id).classed(TEST_LEGEND_MARK, true); 88 | } 89 | 90 | // Legend labels 91 | for (const id of legendLabelIDs) { 92 | svg.select('#' + baseID + id).classed(TEST_LEGEND_LABEL, true); 93 | } 94 | 95 | // Legend title 96 | svg.select('#' + baseID + legendTitleID).classed(TEST_LEGEND_TITLE, true); 97 | 98 | return svg.node(); 99 | } 100 | -------------------------------------------------------------------------------- /examples/annotated-visualizations/multi-view/annotated-vega-lite-weather.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | export async function createVegaMultiView() { 3 | const spec1 = { 4 | $schema: '/examples/data/vega-lite-schema-v5.json', 5 | title: 'Seattle Weather, 2012-2015', 6 | data: { 7 | url: '/examples/data/seattle-weather.csv' 8 | }, 9 | encoding: { 10 | color: { 11 | title: 'Weather', 12 | field: 'weather', 13 | type: 'nominal', 14 | scale: { 15 | domain: ['sun', 'fog', 'drizzle', 'rain', 'snow'], 16 | range: ['#e7ba52', '#a7a7a7', '#aec7e8', '#1f77b4', '#9467bd'] 17 | } 18 | }, 19 | size: { 20 | title: 'Precipitation', 21 | field: 'precipitation', 22 | scale: { type: 'linear', domain: [-1, 50] }, 23 | type: 'quantitative' 24 | }, 25 | x: { 26 | field: 'date', 27 | timeUnit: 'utcyearmonthdate', 28 | title: 'Date', 29 | axis: { format: '%b %Y' } 30 | }, 31 | y: { 32 | title: 'Maximum Daily Temperature (C)', 33 | field: 'temp_max', 34 | scale: { domain: [-5, 40] }, 35 | type: 'quantitative' 36 | } 37 | }, 38 | width: 700, 39 | height: 400, 40 | mark: 'point' 41 | }; 42 | 43 | const spec2 = { 44 | $schema: '/examples/data/vega-lite-schema-v5.json', 45 | title: 'Seattle Weather, 2012-2015', 46 | data: { 47 | url: '/examples/data/seattle-weather.csv' 48 | }, 49 | encoding: { 50 | color: { 51 | field: 'weather', 52 | scale: { 53 | domain: ['sun', 'fog', 'drizzle', 'rain', 'snow'], 54 | range: ['#e7ba52', '#a7a7a7', '#aec7e8', '#1f77b4', '#9467bd'] 55 | } 56 | }, 57 | x: { aggregate: 'max', field: 'wind' }, 58 | y: { title: 'Weather', field: 'weather' } 59 | }, 60 | width: 700, 61 | mark: 'bar' 62 | }; 63 | 64 | const view1 = new vega.View(vega.parse(vegaLite.compile(spec1).spec), { renderer: 'svg' }); 65 | const view2 = new vega.View(vega.parse(vegaLite.compile(spec2).spec), { renderer: 'svg' }); 66 | 67 | const svgText1 = await view1.toSVG(); 68 | const svgText2 = await view2.toSVG(); 69 | 70 | const svg1 = d3.create('div').html(svgText1).select('svg').node(); 71 | const svg2 = d3.create('div').html(svgText2).select('svg').node(); 72 | 73 | return [svg1, svg2]; 74 | } 75 | -------------------------------------------------------------------------------- /examples/annotated-visualizations/observable-plot/annotated-scatter-plot.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | 3 | import { TEST_MARK, TEST_X_AXIS_LABEL, TEST_X_AXIS_TICK, TEST_X_AXIS_TITLE, TEST_Y_AXIS_LABEL, TEST_Y_AXIS_TICK, TEST_Y_AXIS_TITLE } from '../../../test/util/test-constants.js'; 4 | 5 | export async function createScatterPlot() { 6 | let data = await d3.csv('/examples/data/penguins.csv', d3.autoType); 7 | data = data.filter(d => d.sex != null); 8 | 9 | const p = Plot.plot({ 10 | inset: 8, 11 | grid: true, 12 | color: { 13 | legend: false, 14 | style: { marginLeft: 500 } 15 | }, 16 | marks: [ 17 | Plot.dot(data, { x: 'flipper_length_mm', y: 'body_mass_g', stroke: 'sex' }), 18 | Plot.axisY({ label: '↑ body_mass_g', marginLeft: 50 }) 19 | ] 20 | }); 21 | 22 | d3.select(p).attr('id', 'chart1'); 23 | 24 | // Add annotations 25 | const svg = d3.select(p); 26 | svg.selectAll('circle').classed(TEST_MARK, true); // Marks 27 | 28 | // x-axis ticks 29 | svg.selectAll('g').filter(function() { 30 | const attr = d3.select(this).attr('aria-label'); 31 | return attr === 'x-grid' || attr === 'x-axis tick'; 32 | }).selectAll('line, path').classed(TEST_X_AXIS_TICK, true); 33 | 34 | // x-axis labels 35 | svg.selectAll('g').filter(function() { 36 | return d3.select(this).attr('aria-label') === 'x-axis tick label'; 37 | }).selectAll('text').classed(TEST_X_AXIS_LABEL, true); 38 | 39 | // x-axis title 40 | svg.selectAll('g').filter(function() { 41 | return d3.select(this).attr('aria-label') === 'x-axis label'; 42 | }).select('text').classed(TEST_X_AXIS_TITLE, true); 43 | 44 | // y-axis ticks 45 | svg.selectAll('g').filter(function() { 46 | const attr = d3.select(this).attr('aria-label'); 47 | return attr === 'y-grid' || attr === 'y-axis tick'; 48 | }).selectAll('line, path').classed(TEST_Y_AXIS_TICK, true); 49 | 50 | // y-axis labels 51 | svg.selectAll('g').filter(function() { 52 | return d3.select(this).attr('aria-label') === 'y-axis tick label'; 53 | }).selectAll('text').classed(TEST_Y_AXIS_LABEL, true); 54 | 55 | // y-axis title 56 | svg.selectAll('g').filter(function() { 57 | return d3.select(this).attr('aria-label') === 'y-axis label'; 58 | }).select('text').classed(TEST_Y_AXIS_TITLE, true); 59 | 60 | return p; 61 | } 62 | -------------------------------------------------------------------------------- /examples/data/XYZ.csv: -------------------------------------------------------------------------------- 1 | year,value 2 | 2011,45 3 | 2012,47 4 | 2013,52 5 | 2014,70 6 | 2015,75 7 | 2016,78 8 | -------------------------------------------------------------------------------- /examples/data/alphabet.csv: -------------------------------------------------------------------------------- 1 | letter,frequency 2 | E,0.12702 3 | T,0.09056 4 | A,0.08167 5 | O,0.07507 6 | I,0.06966 7 | N,0.06749 8 | S,0.06327 9 | H,0.06094 10 | R,0.05987 11 | D,0.04253 12 | L,0.04025 13 | C,0.02782 14 | U,0.02758 15 | M,0.02406 16 | W,0.0236 17 | F,0.02288 18 | G,0.02015 19 | Y,0.01974 20 | P,0.01929 21 | B,0.01492 22 | V,0.00978 23 | K,0.00772 24 | J,0.00153 25 | X,0.0015 26 | Q,0.00095 27 | Z,0.00074 28 | -------------------------------------------------------------------------------- /examples/data/cities-lived.csv: -------------------------------------------------------------------------------- 1 | years,place,lat,lon 2 | 2,New York City,40.71455,-74.007124 3 | 6,San Francisco,37.7771187,-122.4196396 4 | 8,Santa Cruz,36.9740181,-122.0309525 5 | 3,Santa Barbara,34.4193802,-119.6990509 6 | 10,Tucson,32.22155,-110.9697571 7 | 1,Washington DC,38.8903694,-77.0319595 8 | -------------------------------------------------------------------------------- /examples/data/data.csv: -------------------------------------------------------------------------------- 1 | State,Under 5 Years,5 to 13 Years,14 to 17 Years,18 to 24 Years,25 to 44 Years,45 to 64 Years,65 Years and Over 2 | AL,310504,552339,259034,450818,1231572,1215966,641667 3 | AK,52083,85640,42153,74257,198724,183159,50277 4 | AZ,515910,828669,362642,601943,1804762,1523681,862573 5 | AR,202070,343207,157204,264160,754420,727124,407205 6 | CA,2704659,4499890,2159981,3853788,10604510,8819342,4114496 7 | CO,358280,587154,261701,466194,1464939,1290094,511094 8 | CT,211637,403658,196918,325110,916955,968967,478007 9 | DE,59319,99496,47414,84464,230183,230528,121688 10 | DC,36352,50439,25225,75569,193557,140043,70648 11 | FL,1140516,1938695,925060,1607297,4782119,4746856,3187797 12 | GA,740521,1250460,557860,919876,2846985,2389018,981024 13 | HI,87207,134025,64011,124834,356237,331817,190067 14 | ID,121746,201192,89702,147606,406247,375173,182150 15 | IL,894368,1558919,725973,1311479,3596343,3239173,1575308 16 | IN,443089,780199,361393,605863,1724528,1647881,813839 17 | IA,201321,345409,165883,306398,750505,788485,444554 18 | KS,202529,342134,155822,293114,728166,713663,366706 19 | KY,284601,493536,229927,381394,1179637,1134283,565867 20 | LA,310716,542341,254916,471275,1162463,1128771,540314 21 | ME,71459,133656,69752,112682,331809,397911,199187 22 | MD,371787,651923,316873,543470,1556225,1513754,679565 23 | MA,383568,701752,341713,665879,1782449,1751508,871098 24 | MI,625526,1179503,585169,974480,2628322,2706100,1304322 25 | MN,358471,606802,289371,507289,1416063,1391878,650519 26 | MS,220813,371502,174405,305964,764203,730133,371598 27 | MO,399450,690476,331543,560463,1569626,1554812,805235 28 | MT,61114,106088,53156,95232,236297,278241,137312 29 | NE,132092,215265,99638,186657,457177,451756,240847 30 | NV,199175,325650,142976,212379,769913,653357,296717 31 | NH,75297,144235,73826,119114,345109,388250,169978 32 | NJ,557421,1011656,478505,769321,2379649,2335168,1150941 33 | NM,148323,241326,112801,203097,517154,501604,260051 34 | NY,1208495,2141490,1058031,1999120,5355235,5120254,2607672 35 | NC,652823,1097890,492964,883397,2575603,2380685,1139052 36 | ND,41896,67358,33794,82629,154913,166615,94276 37 | OH,743750,1340492,646135,1081734,3019147,3083815,1570837 38 | OK,266547,438926,200562,369916,957085,918688,490637 39 | OR,243483,424167,199925,338162,1044056,1036269,503998 40 | PA,737462,1345341,679201,1203944,3157759,3414001,1910571 41 | RI,60934,111408,56198,114502,277779,282321,147646 42 | SC,303024,517803,245400,438147,1193112,1186019,596295 43 | SD,58566,94438,45305,82869,196738,210178,116100 44 | TN,416334,725948,336312,550612,1719433,1646623,819626 45 | TX,2027307,3277946,1420518,2454721,7017731,5656528,2472223 46 | UT,268916,413034,167685,329585,772024,538978,246202 47 | VT,32635,62538,33757,61679,155419,188593,86649 48 | VA,522672,887525,413004,768475,2203286,2033550,940577 49 | WA,433119,750274,357782,610378,1850983,1762811,783877 50 | WV,105435,189649,91074,157989,470749,514505,285067 51 | WI,362277,640286,311849,553914,1487457,1522038,750146 52 | WY,38253,60890,29314,53980,137338,147279,65614 53 | -------------------------------------------------------------------------------- /examples/data/stateslived.csv: -------------------------------------------------------------------------------- 1 | state,visited 2 | Alabama,0 3 | Alaska,0 4 | Arkansas,0 5 | Arizona,2 6 | California,2 7 | Colorado,1 8 | Connecticut,1 9 | Delaware,0 10 | Florida,0 11 | Georgia,0 12 | Hawaii,0 13 | Iowa,0 14 | Idaho,1 15 | Illinois,1 16 | Indiana,0 17 | Kansas,0 18 | Kentucky,0 19 | Louisiana,0 20 | Maine,1 21 | Maryland,1 22 | Massachusetts,1 23 | Michigan,0 24 | Minnesota,1 25 | Missouri,0 26 | Mississippi,0 27 | Montana,1 28 | North Carolina,0 29 | North Dakota,0 30 | Nebraska,0 31 | New Hampshire,1 32 | New Jersey,0 33 | New Mexico,1 34 | Nevada,1 35 | New York,2 36 | Ohio,1 37 | Oklahoma,0 38 | Oregon,1 39 | Pennsylvania,1 40 | Rhode Island,1 41 | South Carolina,0 42 | South Dakota,0 43 | Tennessee,0 44 | Texas,0 45 | Utah,1 46 | Virginia,0 47 | Vermont,0 48 | Washington,1 49 | Wisconsin,0 50 | West Virginia,0 51 | Wyoming,1 -------------------------------------------------------------------------------- /examples/visualizations/d3/bar-chart.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | // eslint-disable-next-line no-unused-vars 3 | async function createBarChart() { 4 | // set the dimensions and margins of the graph 5 | const margin = { top: 30, right: 30, bottom: 70, left: 60 }; 6 | const width = 720 - margin.left - margin.right; 7 | const height = 720 - margin.top - margin.bottom; 8 | 9 | // append the svg object to the body of the page 10 | let svg = d3.create('svg') 11 | .attr('width', width + margin.left + margin.right) 12 | .attr('height', height + margin.top + margin.bottom) 13 | .attr('id', 'chart'); 14 | 15 | const r = svg; 16 | svg = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 17 | 18 | // Parse the Data 19 | const data = await d3.csv('https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/7_OneCatOneNum_header.csv'); 20 | 21 | // sort data 22 | data.sort(function(b, a) { 23 | return a.Value - b.Value; 24 | }); 25 | 26 | // X axis 27 | const x = d3.scaleBand() 28 | .range([0, width]) 29 | .domain(data.map(function(d) { return d.Country; })) 30 | .padding(0.2); 31 | svg.append('g') 32 | .attr('transform', 'translate(0,' + height + ')') 33 | .call(d3.axisBottom(x)) 34 | .selectAll('text') 35 | .attr('transform', 'translate(-10,0)rotate(-45)') 36 | .style('text-anchor', 'end'); 37 | 38 | // Add Y axis 39 | const y = d3.scaleLinear() 40 | .domain([0, 13000]) 41 | .range([height, 0]); 42 | svg.append('g') 43 | .call(d3.axisLeft(y)); 44 | 45 | // Bars 46 | svg.selectAll('mybar') 47 | .data(data) 48 | .enter() 49 | .append('rect') 50 | .attr('x', function(d) { return x(d.Country); }) 51 | .attr('y', function(d) { return y(d.Value); }) 52 | .attr('width', x.bandwidth()) 53 | .attr('height', function(d) { return height - y(d.Value); }) 54 | .attr('fill', '#69b3a2'); 55 | 56 | return r.node(); 57 | } 58 | -------------------------------------------------------------------------------- /examples/visualizations/d3/custom-map.js: -------------------------------------------------------------------------------- 1 | function createMap() { 2 | //Width and height of map 3 | var width = 960; 4 | var height = 500; 5 | 6 | // D3 Projection 7 | var projection = d3.geoAlbersUsa() 8 | .translate([width/2, height/2]) // translate to center of screen 9 | .scale([1000]); // scale things down so see entire US 10 | 11 | // Define path generator 12 | var path = d3.geoPath() // path generator that will convert GeoJSON to SVG paths 13 | .projection(projection); // tell path generator to use albersUsa projection 14 | 15 | 16 | // Define linear scale for output 17 | var color = d3.scaleLinear() 18 | .range(["rgb(213,222,217)","rgb(69,173,168)","rgb(84,36,55)","rgb(217,91,67)"]); 19 | 20 | var legendText = ["Cities Lived", "States Lived", "States Visited", "Nada"]; 21 | 22 | //Create SVG element and append map to the SVG 23 | var svg = d3.select("#map") 24 | .append("svg") 25 | .attr("id", "mapsvg") 26 | .attr("width", width) 27 | .attr("height", height); 28 | 29 | // Append Div for tooltip to SVG 30 | // var div = d3.select("body") 31 | // .append("div") 32 | // .attr("class", "tooltip") 33 | // .style("opacity", 0); 34 | 35 | // Load in my states data! 36 | d3.csv("https://raw.githubusercontent.com/Luke-S-Snyder/divi/main/examples/data/stateslived.csv").then(function(data) { 37 | color.domain([0,1,2,3]); // setting the range of the input data 38 | // Load GeoJSON data and merge with states data 39 | d3.json("https://gist.githubusercontent.com/michellechandra/0b2ce4923dc9b5809922/raw/a476b9098ba0244718b496697c5b350460d32f99/us-states.json").then(function(json) { 40 | 41 | // Loop through each state data value in the .csv file 42 | for (var i = 0; i < data.length; i++) { 43 | 44 | // Grab State Name 45 | var dataState = data[i].state; 46 | 47 | // Grab data value 48 | var dataValue = data[i].visited; 49 | 50 | // Find the corresponding state inside the GeoJSON 51 | for (var j = 0; j < json.features.length; j++) { 52 | var jsonState = json.features[j].properties.name; 53 | 54 | if (dataState == jsonState) { 55 | // Copy the data value into the JSON 56 | json.features[j].properties.visited = dataValue; 57 | 58 | // Stop looking through the JSON 59 | break; 60 | } 61 | } 62 | } 63 | 64 | // Bind the data to the SVG and create one path per GeoJSON feature 65 | svg.selectAll("path") 66 | .data(json.features) 67 | .enter() 68 | .append("path") 69 | .attr("d", path) 70 | .style("stroke", "#fff") 71 | .style("stroke-width", "1") 72 | .style("fill", function(d) { 73 | // Get data value 74 | var value = d.properties.visited; 75 | 76 | if (value) { 77 | //If value exists… 78 | return color(value); 79 | } else { 80 | //If value is undefined… 81 | return "rgb(213,222,217)"; 82 | } 83 | }); 84 | 85 | // Map the cities I have lived in! 86 | d3.csv("https://raw.githubusercontent.com/Luke-S-Snyder/divi/main/examples/data/cities-lived.csv").then(function(data) { 87 | 88 | svg.selectAll("circle") 89 | .data(data) 90 | .enter() 91 | .append("circle") 92 | .attr("cx", function(d) { 93 | return projection([d.lon, d.lat])[0]; 94 | }) 95 | .attr("cy", function(d) { 96 | return projection([d.lon, d.lat])[1]; 97 | }) 98 | .attr("r", function(d) { 99 | return Math.sqrt(d.years) * 4; 100 | }) 101 | .style("fill", "rgb(217,91,67)") 102 | .style("opacity", 0.85) 103 | 104 | // // Modified Legend Code from Mike Bostock: http://bl.ocks.org/mbostock/3888852 105 | // var legend = d3.select("body").append("svg") 106 | // .attr("class", "legend") 107 | // .attr("width", 140) 108 | // .attr("height", 200) 109 | // .selectAll("g") 110 | // .data(color.domain().slice().reverse()) 111 | // .enter() 112 | // .append("g") 113 | // .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); 114 | 115 | // legend.append("rect") 116 | // .attr("width", 18) 117 | // .attr("height", 18) 118 | // .style("fill", color); 119 | 120 | // legend.append("text") 121 | // .data(legendText) 122 | // .attr("x", 24) 123 | // .attr("y", 9) 124 | // .attr("dy", ".35em") 125 | // .text(function(d) { return d; }); 126 | divi.hydrate("#mapsvg"); 127 | }); 128 | }); 129 | }); 130 | } 131 | -------------------------------------------------------------------------------- /examples/visualizations/d3/glyph-map.js: -------------------------------------------------------------------------------- 1 | function createGlyphMap() { 2 | // Load in my states data! 3 | d3.json("https://gist.githubusercontent.com/michellechandra/0b2ce4923dc9b5809922/raw/a476b9098ba0244718b496697c5b350460d32f99/us-states.json").then(function(map_data) { 4 | d3.json("https://cdn.jsdelivr.net/npm/us-atlas@3/counties-albers-10m.json").then(function(us){ 5 | // Load GeoJSON data and merge with states data 6 | d3.json("https://api.census.gov/data/2016/acs/acs5/cprofile?get=CP05_2012_2016_001E,NAME&for=county:*").then(function(json) { 7 | 8 | 9 | // D3 Projection 10 | var projection = d3.geoAlbersUsa() 11 | // let path1 = d3.geoPath(d3.geoAlbersUsa().translate([500, 500]).scale(750)); 12 | // .translate([500/2, 500/2]) // translate to center of screen 13 | // .scale([1000]); // scale things down so see entire US 14 | 15 | // Define path generator 16 | var path = d3.geoPath() // path generator that will convert GeoJSON to SVG paths 17 | // .projection(projection); // tell path generator to use albersUsa projection 18 | 19 | let features = new Map(topojson.feature(us, us.objects.counties).features.map(d => [d.id, d])); 20 | let data = json.slice(1).map(([population, name, state, county]) => { 21 | const id = state + county; 22 | const feature = features.get(id); 23 | // console.log(feature) 24 | // console.log([path1.centroid(feature), path.centroid(feature)]) 25 | return { 26 | id, 27 | position: feature && path.centroid(feature), 28 | title: feature && feature.properties.name, 29 | value: +population 30 | }; 31 | }); 32 | // console.log(data) 33 | let length = d3.scaleLinear([0, d3.max(data, d => d.value)], [0, 200]); 34 | let spike = (length, width = 7) => `M${-width / 2},0L0,${-length}L${width / 2},0`; 35 | 36 | const svg = d3.select("#container") 37 | .append("svg") 38 | .attr("id", "chart") 39 | .attr("width", 2000) 40 | .attr('height', 2000); 41 | 42 | // svg.append("path") 43 | // .datum(topojson.feature(us, us.objects.nation)) 44 | // .attr("fill", "#e0e0e0") 45 | // .attr("d", path); 46 | 47 | // svg.append("path") 48 | // .datum(topojson.mesh(us, us.objects.states, (a, b) => a !== b)) 49 | // .attr("fill", "none") 50 | // .attr("stroke", "white") 51 | // .attr("stroke-linejoin", "round") 52 | // .attr("d", path); 53 | 54 | 55 | // Bind the data to the SVG and create one path per GeoJSON feature 56 | // Bind the data to the SVG and create one path per GeoJSON feature 57 | 58 | var t = topojson.feature(us, us.objects.states); 59 | svg.selectAll("path") 60 | .data(t.features) 61 | .enter() 62 | .append("path") 63 | .attr("d", path) 64 | .style("stroke", "#fff") 65 | .style("stroke-width", "1") 66 | .style("fill", function(d) { 67 | // Get data value 68 | var value = d.properties.visited; 69 | value = false; 70 | 71 | if (value) { 72 | //If value exists… 73 | return color(value); 74 | } else { 75 | //If value is undefined… 76 | return "rgb(213,222,217)"; 77 | } 78 | }); 79 | 80 | const legend = svg.append("g") 81 | .attr("fill", "#777") 82 | .attr("text-anchor", "middle") 83 | .attr("font-family", "sans-serif") 84 | .attr("font-size", 10) 85 | .selectAll("g") 86 | .data(length.ticks(4).slice(1).reverse()) 87 | .join("g") 88 | .attr("transform", (d, i) => `translate(${1000 - (i + 1) * 18},525)`); 89 | 90 | legend.append("path") 91 | .attr("fill", "red") 92 | .attr("fill-opacity", 0.3) 93 | .attr("stroke", "red") 94 | .attr("d", d => spike(length(d))); 95 | 96 | legend.append("text") 97 | .attr("dy", "1.3em") 98 | .text(length.tickFormat(4, "s")); 99 | 100 | svg.append("g") 101 | .attr("fill", "red") 102 | .attr("fill-opacity", 0.3) 103 | .attr("stroke", "red") 104 | .selectAll("path") 105 | .data(data 106 | .filter(d => d.position) 107 | .sort((a, b) => d3.ascending(a.position[1], b.position[1]) 108 | || d3.ascending(a.position[0], b.position[0]))) 109 | .join("path") 110 | .attr("transform", d => `translate(${d.position})`) 111 | .attr("d", d => spike(length(d.value))) 112 | .append("title") 113 | .text(d => `${d.title} 114 | ${d3.format(d.value)}`); 115 | 116 | divi.hydrate("#chart") 117 | }); 118 | }); 119 | }); 120 | } 121 | -------------------------------------------------------------------------------- /examples/visualizations/d3/hex-map.js: -------------------------------------------------------------------------------- 1 | function createHexChart() { 2 | var width = 500; 3 | var height = 300; 4 | 5 | // The svg 6 | var svg = d3.select("#container") 7 | .append("svg") 8 | .attr("width", width) 9 | .attr("height", height) 10 | .attr("id", "chart"); 11 | 12 | // Map and projection 13 | var projection = d3.geoMercator() 14 | .scale(350) // This is the zoom 15 | .translate([850, 440]); // You have to play with these values to center your map 16 | 17 | // Path generator 18 | var path = d3.geoPath() 19 | .projection(projection) 20 | 21 | // Load external data and boot 22 | d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/us_states_hexgrid.geojson.json").then(function(data){ 23 | // Draw the map 24 | svg.append("g") 25 | .selectAll("path") 26 | .data(data.features) 27 | .enter() 28 | .append("path") 29 | .attr("fill", "#69a2a2") 30 | .attr("d", path) 31 | .attr("stroke", "white") 32 | 33 | // Add the labels 34 | svg.append("g") 35 | .selectAll("labels") 36 | .data(data.features) 37 | .enter() 38 | .append("text") 39 | .attr("x", function(d){return path.centroid(d)[0]}) 40 | .attr("y", function(d){return path.centroid(d)[1]}) 41 | .text(function(d){ return d.properties.iso3166_2}) 42 | .attr("text-anchor", "middle") 43 | .attr("alignment-baseline", "central") 44 | .style("font-size", 11) 45 | .style("fill", "white") 46 | 47 | divi.hydrate("#chart"); 48 | }) 49 | 50 | } 51 | -------------------------------------------------------------------------------- /examples/visualizations/d3/line-chart.js: -------------------------------------------------------------------------------- 1 | function createLineChart() { 2 | // set the dimensions and margins of the graph 3 | // var margin = {top: 10, right: 30, bottom: 30, left: 60}, 4 | // width = 460 - margin.left - margin.right, 5 | // height = 400 - margin.top - margin.bottom; 6 | var margin = {top: 10, right: 30, bottom: 40, left: 50}, 7 | width = 720 - margin.left - margin.right, 8 | height = 720 - margin.top - margin.bottom 9 | 10 | // append the svg object to the body of the page 11 | var svg_line = d3.select("#container") 12 | .append("svg") 13 | .attr("width", width + margin.left + margin.right) 14 | .attr("height", height + margin.top + margin.bottom) 15 | .attr("id", "chart") 16 | .append("g") 17 | .attr("transform", 18 | "translate(" + margin.left + "," + margin.top + ")"); 19 | 20 | //Read the data 21 | d3.csv("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/3_TwoNumOrdered_comma.csv").then(function(data) { 22 | 23 | // // When reading the csv, I must format variables: 24 | // function(d){ 25 | // return { date : d3.timeParse("%Y-%m-%d")(d.date), value : d.value } 26 | // } 27 | data = data.map(function(d) { 28 | return { date : d3.timeParse("%Y-%m-%d")(d.date), value : d.value } 29 | }); 30 | 31 | // Add X axis --> it is a date format 32 | var x = d3.scaleTime() 33 | .domain(d3.extent(data, function(d) { return d.date; })) 34 | .range([ 0, width ]); 35 | svg_line.append("g") 36 | .attr("transform", "translate(0," + height + ")") 37 | .call(d3.axisBottom(x)); 38 | 39 | // Add Y axis 40 | var y = d3.scaleLinear() 41 | .domain([0, d3.max(data, function(d) { return +d.value; })]) 42 | .range([ height, 0 ]); 43 | svg_line.append("g") 44 | .call(d3.axisLeft(y)); 45 | 46 | // Add the line 47 | svg_line.append("g").append("path") 48 | .datum(data) 49 | .attr("fill", "none") 50 | .attr("stroke", "steelblue") 51 | .attr("stroke-width", 1.5) 52 | .attr("d", d3.line() 53 | .x(function(d) { return x(d.date) }) 54 | .y(function(d) { return y(d.value) }) 55 | ) 56 | 57 | divi.hydrate("#chart"); 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /examples/visualizations/d3/log-chart.js: -------------------------------------------------------------------------------- 1 | function createLogChart() { 2 | var superscript = "⁰¹²³⁴⁵⁶⁷⁸⁹", 3 | formatPower = function(d) { return (d + "").split("").map(function(c) { return superscript[c]; }).join(""); }; 4 | 5 | var margin = {top: 40.5, right: 40.5, bottom: 50.5, left: 60.5}, 6 | width = 720 - margin.left - margin.right, 7 | height = 720 - margin.top - margin.bottom; 8 | 9 | // var x = d3.scaleLinear() 10 | // .domain([0, 100]) 11 | // .range([0, width]); 12 | 13 | // var y = d3.scaleLog() 14 | // .base(Math.E) 15 | // .domain([Math.exp(0), Math.exp(9)]) 16 | // .range([height, 0]); 17 | 18 | let data = d3.range(10).map(function(x) { return [Math.exp(x), x * Math.exp(2.5)]; }); 19 | 20 | var x = d3.scaleLog() 21 | // .base(Math.E) 22 | .domain([Math.exp(0), Math.exp(9)]) 23 | .range([0, width]); 24 | 25 | var y = d3.scaleLinear() 26 | .domain(d3.extent(data.map(d => d[1]))) 27 | .range([height, 0]); 28 | 29 | 30 | var xAxis = d3.axisBottom() 31 | .scale(x) 32 | // .tickFormat(function(d) { return "e" + formatPower(Math.round(Math.log(d))); }); 33 | 34 | var yAxis = d3.axisLeft() 35 | .scale(y); 36 | 37 | var line = d3.line() 38 | .x(function(d) { return x(d[0]); }) 39 | .y(function(d) { return y(d[1]); }); 40 | 41 | var svg = d3.select("#container").append("svg") 42 | .attr("id", "chart") 43 | .attr("width", width + margin.left + margin.right) 44 | .attr("height", height + margin.top + margin.bottom) 45 | .append("g") 46 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 47 | 48 | svg.append("g") 49 | .attr("class", "axis axis--y") 50 | .attr("transform", "translate(-10,0)") 51 | .call(yAxis); 52 | 53 | var gx= svg.append("g") 54 | .attr("class", "axis axis--x") 55 | .attr("transform", "translate(0," + (height + 10) + ")") 56 | .call(xAxis); 57 | 58 | 59 | svg.append("path") 60 | .datum(data) 61 | .attr("class", "line") 62 | .attr("d", line) 63 | .attr("fill", "none") 64 | .attr("stroke", "steelblue") 65 | .attr("strokewidth", "1.5px"); 66 | 67 | // let svg_ = d3.select("svg"); 68 | 69 | // svg_.call(d3.zoom().on("zoom", function({transform}) { 70 | // console.log('here') 71 | // gx.call(xAxis.scale(transform.rescaleX(x))); 72 | // x.range([margin.left, width - margin.right] 73 | // .map(d => transform.applyX(d))); 74 | // svg_.select(".line") 75 | // .attr("d", d3.line() 76 | // .x(function(d) { return x(d[0]); }) 77 | // .y(function(d) { return y(d[1]); }) 78 | // ) 79 | // })); 80 | 81 | divi.hydrate("#chart"); 82 | } 83 | -------------------------------------------------------------------------------- /examples/visualizations/d3/pie-chart.js: -------------------------------------------------------------------------------- 1 | 2 | // set the dimensions and margins of the graph 3 | var width = 450 4 | height = 450 5 | margin = 40 6 | 7 | // The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin. 8 | var radius = Math.min(width, height) / 2 - margin 9 | 10 | // append the svg object to the div called 'my_dataviz' 11 | var svg = d3.select("#vis") 12 | .append("svg") 13 | .attr("width", width) 14 | .attr("height", height) 15 | .attr("id", "svg_plot") 16 | .append("g") 17 | .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); 18 | 19 | // Create dummy data 20 | var data = {a: 9, b: 20, c:30, d:8, e:12} 21 | 22 | // set the color scale 23 | var color = d3.scaleOrdinal() 24 | .domain(data) 25 | .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56"]) 26 | 27 | // Compute the position of each group on the pie: 28 | var pie = d3.pie() 29 | .value(function(d) {return d.value; }) 30 | var data_ready = pie(d3.entries(data)) 31 | 32 | // Build the pie chart: Basically, each part of the pie is a path that we build using the arc function. 33 | svg 34 | .selectAll('whatever') 35 | .data(data_ready) 36 | .enter() 37 | .append('path') 38 | .attr('d', d3.arc() 39 | .innerRadius(0) 40 | .outerRadius(radius) 41 | ) 42 | .attr('fill', function(d){ return(color(d.data.key)) }) 43 | .attr("stroke", "black") 44 | .style("stroke-width", "2px") 45 | .style("opacity", 0.7) 46 | -------------------------------------------------------------------------------- /examples/visualizations/d3/scatter-plot.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | // eslint-disable-next-line no-unused-vars 3 | async function createScatterPlot() { 4 | // set the dimensions and margins of the graph 5 | const margin = { top: 10, right: 30, bottom: 40, left: 50 }; 6 | const width = 720 - margin.left - margin.right; 7 | const height = 720 - margin.top - margin.bottom; 8 | 9 | // append the svg object to the body of the page 10 | let svg = d3.create('svg') 11 | .attr('width', width + margin.left + margin.right) 12 | .attr('height', height + margin.top + margin.bottom) 13 | .attr('id', 'chart'); 14 | 15 | const r = svg; 16 | svg = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 17 | 18 | // Read the data 19 | const data = await d3.csv('https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/iris.csv'); 20 | 21 | // Add X axis 22 | const x = d3.scaleLinear() 23 | .domain([4 * 0.95, 8 * 1.001]) 24 | .range([0, width]); 25 | 26 | const xAxis = svg.append('g') 27 | .attr('transform', 'translate(0,' + height + ')') 28 | .call(d3.axisBottom(x).tickSize(-height * 1.3).ticks(10)); 29 | 30 | xAxis.select('.domain').remove(); 31 | 32 | // Add Y axis 33 | const y = d3.scaleLinear() 34 | .domain([-0.001, 9 * 1.01]) 35 | .range([height, 0]) 36 | .nice(); 37 | 38 | svg.append('g') 39 | .call(d3.axisLeft(y).tickSize(-width * 1.3).ticks(7)) 40 | .select('.domain').remove(); 41 | 42 | // Customization 43 | svg.selectAll('.tick line').attr('stroke', 'black').attr('opacity', 0.3); 44 | 45 | // Add X axis label: 46 | svg.append('text') 47 | .attr('text-anchor', 'end') 48 | .attr('x', width / 2 + margin.left) 49 | .attr('y', height + margin.top + 20) 50 | .text('Sepal Length'); 51 | 52 | // Y axis label: 53 | svg.append('text') 54 | .attr('text-anchor', 'end') 55 | .attr('transform', 'rotate(-90)') 56 | .attr('y', -margin.left + 20) 57 | .attr('x', -margin.top - height / 2 + 20) 58 | .text('Petal Length'); 59 | 60 | // Color scale: give me a specie name, I return a color 61 | const color = d3.scaleOrdinal() 62 | .domain(['setosa', 'versicolor', 'virginica']) 63 | .range(['#F8766D', '#00BA38', '#619CFF']); 64 | 65 | // Add dots 66 | svg.append('g') 67 | .selectAll('dot') 68 | .data(data) 69 | .enter() 70 | .append('circle') 71 | .attr('cx', function(d) { return x(d.Sepal_Length); }) 72 | .attr('cy', function(d) { return y(d.Petal_Length); }) 73 | .attr('r', 5) 74 | .style('fill', function(d) { return color(d.Species); }); 75 | 76 | return r.node(); 77 | } 78 | -------------------------------------------------------------------------------- /examples/visualizations/d3/stacked-area-chart.js: -------------------------------------------------------------------------------- 1 | function createStackedChart() { 2 | // // set the dimensions and margins of the graph 3 | // var margin = {top: 20, right: 30, bottom: 30, left: 55}, 4 | // width = 460 - margin.left - margin.right, 5 | // height = 400 - margin.top - margin.bottom; 6 | let margin = {top: 10, right: 30, bottom: 40, left: 50}, 7 | width = 720 - margin.left - margin.right, 8 | height = 720 - margin.top - margin.bottom 9 | 10 | // append the svg object to the body of the page 11 | let svg = d3.select("#container") 12 | .append("svg") 13 | .attr("width", width + margin.left + margin.right) 14 | .attr("height", height + margin.top + margin.bottom) 15 | .attr("id", "chart") 16 | .append("g") 17 | .attr("transform", 18 | "translate(" + margin.left + "," + margin.top + ")"); 19 | 20 | // Parse the Data 21 | d3.csv("https://raw.githubusercontent.com/holtzy/data_to_viz/master/Example_dataset/5_OneCatSevNumOrdered_wide.csv").then(function(data) { 22 | 23 | // List of groups = header of the csv files 24 | let keys = data.columns.slice(1) 25 | 26 | // Add X axis 27 | let x = d3.scaleLinear() 28 | .domain(d3.extent(data, function(d) { return d.year; })) 29 | .range([ 0, width ]); 30 | svg.append("g") 31 | .attr("transform", "translate(0," + height + ")") 32 | .call(d3.axisBottom(x).ticks(5)); 33 | 34 | // Add Y axis 35 | let y = d3.scaleLinear() 36 | .domain([0, 200000]) 37 | .range([ height, 0 ]); 38 | svg.append("g") 39 | .call(d3.axisLeft(y)); 40 | 41 | // color palette 42 | let color = d3.scaleOrdinal() 43 | .domain(keys) 44 | .range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf']) 45 | 46 | //stack the data? 47 | let stackedData = d3.stack() 48 | .keys(keys) 49 | (data) 50 | //console.log("This is the stack result: ", stackedData) 51 | 52 | // Show the areas 53 | svg 54 | .append("g") 55 | .selectAll("mylayers") 56 | .data(stackedData) 57 | .enter() 58 | .append("path") 59 | .style("fill", function(d) { return color(d.key); }) 60 | .attr("d", d3.area() 61 | .x(function(d, i) { return x(d.data.year); }) 62 | .y0(function(d) { return y(d[0]); }) 63 | .y1(function(d) { return y(d[1]); }) 64 | ) 65 | 66 | divi.hydrate("#chart"); 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /examples/visualizations/d3/stacked-bar-chart.js: -------------------------------------------------------------------------------- 1 | function createStackedBarChart() { 2 | // var svg = d3.select("stackedbarsvg"), 3 | var margin = {top: 20, right: 20, bottom: 30, left: 40}, 4 | width = 1000 - margin.left - margin.right, 5 | height = 720 - margin.top - margin.bottom; 6 | // g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 7 | 8 | let svg = d3.select("#container") 9 | .append("svg") 10 | .attr("width", width + margin.left + margin.right) 11 | .attr("height", height + margin.top + margin.bottom) 12 | .attr("id", "chart") 13 | .append("g") 14 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")") 15 | 16 | var x = d3.scaleBand() 17 | .rangeRound([0, width]) 18 | .paddingInner(0.05) 19 | .align(0.1); 20 | 21 | var y = d3.scaleLinear() 22 | .rangeRound([height, 0]); 23 | 24 | var z = d3.scaleOrdinal() 25 | .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]); 26 | 27 | d3.csv("../../../examples/data/data.csv").then(function(data) { 28 | for (const d of data) { 29 | for (i = 1, t = 0; i < data.columns.length; ++i) t += d[data.columns[i]] = +d[data.columns[i]]; 30 | d.total = t; 31 | } 32 | 33 | var keys = data.columns.slice(1); 34 | 35 | data.sort(function(a, b) { return b.total - a.total; }); 36 | x.domain(data.map(function(d) { return d.State; })); 37 | y.domain([0, d3.max(data, function(d) { return d.total; })]).nice(); 38 | z.domain(keys); 39 | 40 | svg.append("g") 41 | .selectAll("g") 42 | .data(d3.stack().keys(keys)(data)) 43 | .enter().append("g") 44 | .attr("fill", function(d) { return z(d.key); }) 45 | .selectAll("rect") 46 | .data(function(d) { return d; }) 47 | .enter().append("rect") 48 | .attr("x", function(d) { return x(d.data.State); }) 49 | .attr("y", function(d) { return y(d[1]); }) 50 | .attr("height", function(d) { return y(d[0]) - y(d[1]); }) 51 | .attr("width", x.bandwidth()); 52 | 53 | svg.append("g") 54 | .attr("class", "axis") 55 | .attr("transform", "translate(0," + height + ")") 56 | .call(d3.axisBottom(x)) 57 | .select(".domain").remove();; 58 | 59 | svg.append("g") 60 | .attr("class", "axis") 61 | .call(d3.axisLeft(y).ticks(null, "s")) 62 | .select(".domain").remove() 63 | .append("text") 64 | .attr("x", 2) 65 | .attr("y", y(y.ticks().pop()) + 0.5) 66 | .attr("dy", "0.32em") 67 | .attr("fill", "#000") 68 | .attr("font-weight", "bold") 69 | .attr("text-anchor", "start") 70 | .text("Population"); 71 | 72 | var legend = svg.append("g") 73 | .attr("font-family", "sans-serif") 74 | .attr("font-size", 10) 75 | .attr("text-anchor", "end") 76 | .selectAll("g") 77 | .data(keys.slice().reverse()) 78 | .enter().append("g") 79 | .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; }); 80 | 81 | legend.append("rect") 82 | .attr("x", width - 19) 83 | .attr("width", 19) 84 | .attr("height", 19) 85 | .attr("fill", z); 86 | 87 | legend.append("text") 88 | .attr("x", width - 24) 89 | .attr("y", 9.5) 90 | .attr("dy", "0.32em") 91 | .text(function(d) { return d; }); 92 | 93 | divi.hydrate("#chart"); 94 | }); 95 | } -------------------------------------------------------------------------------- /examples/visualizations/ggplot2/multi-view-setup/excel-scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/divi/41890655aef95e5f6f1f51c930374739da6a1954/examples/visualizations/ggplot2/multi-view-setup/excel-scatter.png -------------------------------------------------------------------------------- /examples/visualizations/ggplot2/multi-view-setup/multi-view-setup.R: -------------------------------------------------------------------------------- 1 | library(ggplot2) 2 | library(svglite) 3 | library(tidyverse) 4 | # theme_set(theme_minimal()) 5 | theme_set(theme_gray() + theme(legend.key=element_blank())) 6 | 7 | X <- read.csv(url("https://vega.github.io/vega-datasets/data/seattle-weather.csv")) 8 | X$date <- as.Date(X$date, format="%Y-%m-%d") 9 | X$cycle <- as.Date(format(X$date, format="%b %d"), format='%b %d') 10 | X <- na.omit(X) 11 | library(lubridate) 12 | year(X$cycle) <- 1990 13 | X 14 | scatter1 <- ggplot(X, aes(x = temp_max, y = precipitation, color = weather)) + 15 | geom_point(size=10) + 16 | # geom_point(aes(size=precipitation)) + 17 | # scale_x_date(date_labels = "%Y-%m-%d") + 18 | # ggtitle('Seattle Weather Data 2012-2015') + 19 | ylab('Precip. (in.)') + 20 | xlab('Max. temp') + 21 | theme(plot.title = element_text(size=50)) + 22 | theme(axis.text = element_text(size=40)) + 23 | theme(axis.title = element_text(size=35)) + 24 | theme(legend.title = element_text(size=35)) + 25 | theme(legend.text = element_text(size=40)) + 26 | theme(legend.key.size = unit(5, 'cm')) + 27 | theme(legend.spacing.x = unit(0.5, 'cm')) + 28 | theme(legend.position = 'right') + 29 | guides(colour = guide_legend(title = 'Weather', override.aes = list(size=10))) 30 | # theme(legend.justification = 'top') 31 | scatter1 32 | 33 | write.csv(X, '~/GitHub/divi/examples/data/seattle-weather.csv', row.names=FALSE) 34 | ggsave(file="~/GitHub/divi/examples/visualizations/ggplot2/multi-view-setup/scatter.svg", plot=scatter1, width=40, height=15) 35 | 36 | X2 <- X 37 | X2$cycle <- format(X2$cycle, format='%b %d') 38 | X2 <- X2 %>% 39 | group_by(cycle) %>% 40 | summarise(precipitation = mean(precipitation)) 41 | 42 | X2$cycle <- as.Date(X2$cycle, format='%b %d') 43 | 44 | scatter2 <- ggplot(X2, aes(x = cycle, y = precipitation)) + 45 | geom_line(aes()) + 46 | # geom_point(aes(size=precipitation)) + 47 | scale_x_date(date_labels = "%b %d") + 48 | ggtitle('Seattle Weather Data 2012-2015') + 49 | xlab('Date') + 50 | ylab('Daily Avg. Precipitation (in.)') + 51 | theme(plot.title = element_text(size=20)) + 52 | theme(axis.text = element_text(size=12)) + 53 | theme(axis.title = element_text(size=15)) + 54 | theme(legend.title = element_text(size=15)) + 55 | theme(legend.text = element_text(size=15)) + 56 | theme(legend.key.size = unit(1, 'cm')) + 57 | guides(colour = guide_legend(title = 'Weather', override.aes = list(size=3))) 58 | # theme(legend.justification = 'top') 59 | scatter2 60 | 61 | ggsave(file="~/GitHub/divi/examples/visualizations/ggplot2/multi-view-setup/line.svg", plot=scatter2, width=10.75, height=6) 62 | -------------------------------------------------------------------------------- /examples/visualizations/ggplot2/multi-view-setup/seattle-weather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/divi/41890655aef95e5f6f1f51c930374739da6a1954/examples/visualizations/ggplot2/multi-view-setup/seattle-weather.png -------------------------------------------------------------------------------- /examples/visualizations/ggplot2/stock-multiline.R: -------------------------------------------------------------------------------- 1 | library(ggplot2) 2 | library(svglite) 3 | library(tidyverse) 4 | theme_set(theme_minimal()) 5 | 6 | X <- read.csv(url("https://vega.github.io/vega-datasets/data/seattle-weather.csv")) 7 | X$date <- as.Date(X$date,format="%Y-%m-%d") 8 | 9 | X1 <- X %>% 10 | filter(weather == "rain") 11 | # X 12 | 13 | image <- ggplot(X1, aes(x = date, y = temp_max)) + 14 | geom_point() + 15 | scale_x_date(date_labels = "%b %Y") 16 | #ggtitle('Rain') 17 | image 18 | # image <- ggplot(X, aes(x = date, y = price, group = symbol, color = symbol)) + 19 | # geom_line() + 20 | # scale_x_date(date_labels = "%b %d %Y") 21 | # ggsave(file="weather-scatter.svg", plot=image, width=12, height=7) 22 | # ggsave(file="weather-scatter2.svg", plot=image, width=14, height=8) 23 | 24 | ggsave(file="weather-scatter3.svg", plot=image, width=12, height=6) 25 | 26 | X2 <- X %>% 27 | filter(weather == "sun") 28 | 29 | image <- ggplot(X1, aes(x = date, y = temp_min)) + 30 | geom_point() + 31 | scale_x_date(date_labels = "%b %Y") 32 | # ggtitle('Sun') 33 | image 34 | ggsave(file="weather-scatter4.svg", plot=image, width=12, height=6) 35 | -------------------------------------------------------------------------------- /examples/visualizations/matplotlib/bar-chart.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt; plt.rcdefaults() 2 | import numpy as np 3 | import pandas as pd 4 | import matplotlib.pyplot as plt 5 | 6 | df = pd.read_csv('../../data/seattle-weather.csv') 7 | df = df[['weather', 'temp_max']] 8 | df = df.groupby(['weather'], as_index=False).sum() 9 | 10 | bar_widths = df['temp_max'].to_numpy() 11 | 12 | 13 | plt.barh(np.arange(len(df['weather'].to_numpy())), df['temp_max'].to_numpy(), align='center') 14 | plt.yticks(np.arange(len(df['weather'].to_numpy())), df['weather'].to_numpy()) 15 | plt.xlabel('Sum of maximum temp.') 16 | plt.ylabel('Weather') 17 | plt.rcParams['svg.fonttype'] = 'none' 18 | plt.grid() 19 | plt.savefig('bars.svg') 20 | -------------------------------------------------------------------------------- /examples/visualizations/matplotlib/bars2.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 225 | -------------------------------------------------------------------------------- /examples/visualizations/matplotlib/multi-view-setup/bar-charts.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt; plt.rcdefaults() 2 | import numpy as np 3 | import pandas as pd 4 | import matplotlib as m 5 | import matplotlib.pyplot as plt 6 | 7 | font = {'size': 50} 8 | 9 | m.rc('font', **font) 10 | 11 | df = pd.read_csv('../../../data/seattle-weather.csv') 12 | 13 | # _df1 = df[['weather', 'wind']] 14 | # _df1 = _df1.groupby(['weather'], as_index=False).mean() 15 | 16 | # bar_widths = _df1['wind'].to_numpy() 17 | 18 | # plt.barh(np.arange(len(_df1['weather'].to_numpy())), _df1['wind'].to_numpy(), align='center', zorder=3) 19 | # plt.yticks(np.arange(len(_df1['weather'].to_numpy())), _df1['weather'].to_numpy()) 20 | # plt.xlabel('Avg., wind') 21 | # plt.ylabel('Weather') 22 | # plt.xlim([0, 8]) 23 | # plt.rcParams['svg.fonttype'] = 'none' 24 | # plt.tight_layout() 25 | # plt.grid(zorder=0, linewidth=0.3) 26 | 27 | # plt.savefig('bars1.svg') 28 | 29 | plt.clf() 30 | _df = df[['weather', 'precipitation']] 31 | # _df = df[(df['weather'] == 'rain') | (df['weather'] == 'snow')] 32 | _df = _df.groupby(['weather'], as_index=False).sum() 33 | 34 | bar_widths = _df['precipitation'].to_numpy() 35 | 36 | plt.figure(figsize=(30, 10)) 37 | plt.barh(np.arange(len(_df['weather'].to_numpy())), _df['precipitation'].to_numpy(), align='center', zorder=3) 38 | plt.yticks(np.arange(len(_df['weather'].to_numpy())), _df['weather'].to_numpy()) 39 | plt.xlabel('Sum, precipitation') 40 | plt.ylabel('Weather') 41 | plt.rcParams['svg.fonttype'] = 'none' 42 | plt.tight_layout() 43 | plt.grid(zorder=0, linewidth=0.4) 44 | plt.savefig('bars_test.svg') 45 | -------------------------------------------------------------------------------- /examples/visualizations/matplotlib/multi-view.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt; plt.rcdefaults() 2 | import numpy as np 3 | import pandas as pd 4 | import matplotlib.pyplot as plt 5 | 6 | df = pd.read_csv('../../data/seattle-weather.csv') 7 | 8 | _df = df[['weather', 'temp_max']] 9 | _df = _df.groupby(['weather'], as_index=False).count() 10 | 11 | bar_widths = _df['temp_max'].to_numpy() 12 | 13 | plt.barh(np.arange(len(_df['weather'].to_numpy())), _df['temp_max'].to_numpy(), align='center', zorder=3) 14 | plt.yticks(np.arange(len(_df['weather'].to_numpy())), _df['weather'].to_numpy()) 15 | plt.xlabel('# of Records') 16 | plt.ylabel('Weather') 17 | plt.rcParams['svg.fonttype'] = 'none' 18 | plt.tight_layout() 19 | plt.grid(zorder=0, linewidth=0.4) 20 | # plt.savefig('bars1.svg') 21 | 22 | plt.clf() 23 | _df = df[['weather', 'temp_max']] 24 | _df = df[(df['weather'] == 'rain') | (df['weather'] == 'snow')] 25 | _df = _df.groupby(['weather'], as_index=False).std() 26 | 27 | bar_widths = _df['temp_max'].to_numpy() 28 | 29 | plt.barh(np.arange(len(_df['weather'].to_numpy())), _df['temp_max'].to_numpy(), align='center', zorder=3) 30 | plt.yticks(np.arange(len(_df['weather'].to_numpy())), _df['weather'].to_numpy()) 31 | plt.xlabel('Std temp_max') 32 | plt.ylabel('Weather') 33 | plt.rcParams['svg.fonttype'] = 'none' 34 | plt.tight_layout() 35 | plt.grid(zorder=0, linewidth=0.4) 36 | plt.savefig('bars2.svg') 37 | -------------------------------------------------------------------------------- /examples/visualizations/multi-view/d3-ggplot2.js: -------------------------------------------------------------------------------- 1 | async function createGgplot2D3() { 2 | let svg1 = await fetch('/examples/visualizations/ggplot2/stock-multiline.svg'); 3 | svg1 = await svg1.text(); 4 | 5 | document.querySelector("#chart1").innerHTML = svg1; 6 | document.querySelector("#chart1 svg").id = "chart1"; 7 | 8 | divi.hydrate(['#chart1']); 9 | } 10 | -------------------------------------------------------------------------------- /examples/visualizations/multi-view/gg-matplot.js: -------------------------------------------------------------------------------- 1 | async function createGgMatplot() { 2 | let svg1 = await fetch('/examples/visualizations/ggplot2/weather-scatter2.svg'); 3 | svg1 = await svg1.text(); 4 | 5 | let svg2 = await fetch('/examples/visualizations/matplotlib/bars1.svg'); 6 | svg2 = await svg2.text(); 7 | 8 | let svg3 = await fetch('/examples/visualizations/matplotlib/bars2.svg'); 9 | svg3 = await svg3.text(); 10 | 11 | document.querySelector("#chart1").innerHTML = svg1; 12 | document.querySelector("#chart1 svg").id = "chart1"; 13 | 14 | document.querySelector("#chart2").innerHTML = svg2; 15 | document.querySelector("#chart2 svg").id = "chart2"; 16 | 17 | document.querySelector("#chart3").innerHTML = svg3; 18 | document.querySelector("#chart3 svg").id = "chart3"; 19 | 20 | divi.hydrate(['#chart1', '#chart2', '#chart3']); 21 | } 22 | -------------------------------------------------------------------------------- /examples/visualizations/multi-view/gg-weather-facet.js: -------------------------------------------------------------------------------- 1 | async function createGgFacet() { 2 | let svg1 = await fetch('/examples/visualizations/ggplot2/weather-scatter3.svg'); 3 | svg1 = await svg1.text(); 4 | 5 | let svg2 = await fetch('/examples/visualizations/ggplot2/weather-scatter4.svg'); 6 | svg2 = await svg2.text(); 7 | 8 | document.querySelector("#chart1").innerHTML = svg1; 9 | document.querySelector("#chart1 svg").id = "chart1"; 10 | 11 | document.querySelector("#chart2").innerHTML = svg2; 12 | document.querySelector("#chart2 svg").id = "chart2"; 13 | 14 | divi.hydrate(['#chart1', '#chart2']); 15 | } 16 | -------------------------------------------------------------------------------- /examples/visualizations/multi-view/ggplot2-setup.js: -------------------------------------------------------------------------------- 1 | async function createGgplot2Setup() { 2 | let svg1 = await fetch('/examples/visualizations/ggplot2/multi-view-setup/excel-scatter.svg'); 3 | svg1 = await svg1.text(); 4 | 5 | let svg2 = await fetch('/examples/visualizations/ggplot2/multi-view-setup/scatter.svg'); 6 | svg2 = await svg2.text(); 7 | 8 | let svg3 = await fetch('/examples/visualizations/matplotlib/multi-view-setup/bars_test.svg'); 9 | svg3 = await svg3.text(); 10 | 11 | let svg4 = await fetch('/examples/visualizations/matplotlib/multi-view-setup/bars1.svg'); 12 | svg4 = await svg4.text(); 13 | 14 | document.querySelector("#chart1").innerHTML = svg1; 15 | document.querySelector("#chart1 svg").id = "svg1"; 16 | 17 | document.querySelector("#chart2").innerHTML = svg2; 18 | document.querySelector("#chart2 svg").id = "svg2"; 19 | 20 | document.querySelector("#chart3").innerHTML = svg3; 21 | document.querySelector("#chart3 svg").id = "svg3"; 22 | 23 | // document.querySelector("#chart4").innerHTML = svg4; 24 | // document.querySelector("#chart4 svg").id = "svg4"; 25 | 26 | divi.hydrate(['#svg1', '#svg2', '#svg3'], {url:'/examples/data/seattle-weather.csv'}); 27 | } 28 | -------------------------------------------------------------------------------- /examples/visualizations/multi-view/ggplot2-vegalite.js: -------------------------------------------------------------------------------- 1 | async function createGgplot2VegaLite() { 2 | const spec1 = { 3 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 4 | "title": "Seattle Weather, 2012-2015", 5 | "data": { 6 | "url": "https://vega.github.io/vega-datasets/data/seattle-weather.csv" 7 | }, 8 | "encoding": { 9 | "color": { 10 | "title": "Weather", 11 | "field": "weather", 12 | "type": "nominal", 13 | "scale": { 14 | "domain": ["sun", "fog", "drizzle", "rain", "snow"], 15 | "range": ["#e7ba52", "#a7a7a7", "#aec7e8", "#1f77b4", "#9467bd"] 16 | } 17 | }, 18 | "size": { 19 | "title": "Precipitation", 20 | "field": "precipitation", 21 | "scale": {"type": "linear", "domain": [-1, 50]}, 22 | "type": "quantitative" 23 | }, 24 | "x": { 25 | "field": "date", 26 | "timeUnit": "utcyearmonthdate", 27 | "title": "Date", 28 | "axis": {"format": "%b %Y"} 29 | }, 30 | "y": { 31 | "title": "Maximum Daily Temperature (C)", 32 | "field": "temp_max", 33 | "scale": {"domain": [-5, 40]}, 34 | "type": "quantitative" 35 | } 36 | }, 37 | "width": 700, 38 | "height": 400, 39 | "mark": "point" 40 | } 41 | 42 | let view1 = new vega.View(vega.parse(vegaLite.compile(spec1).spec), { renderer: 'svg' }); 43 | let svg1 = await view1.toSVG(); 44 | 45 | let svg2 = await fetch('/examples/visualizations/ggplot2/weather-scatter.svg'); 46 | svg2 = await svg2.text(); 47 | 48 | document.querySelector("#chart1").innerHTML = svg1; 49 | document.querySelector("#chart1 svg").id = "chart1"; 50 | 51 | document.querySelector("#chart2").innerHTML = svg2; 52 | document.querySelector("#chart2 svg").id = "chart2"; 53 | 54 | divi.hydrate(['#chart1 svg', '#chart2 svg']); 55 | } 56 | -------------------------------------------------------------------------------- /examples/visualizations/multi-view/vega-bar-scatter.js: -------------------------------------------------------------------------------- 1 | async function createVegaBarScatter() { 2 | const spec1 = { 3 | "data": {"url": "https://vega.github.io/vega-datasets/data/cars.json"}, 4 | "mark": "bar", 5 | "encoding": { 6 | "y": {"field": "Horsepower", "bin": true}, 7 | "x": {"aggregate": "sum", "field": "Acceleration"}, 8 | // "color": {"field": "Origin", "legend": null}, 9 | }, 10 | width: 200, 11 | height: 200 12 | }; 13 | 14 | const spec2 = { 15 | "data": {"url": "https://vega.github.io/vega-datasets/data/cars.json"}, 16 | "mark": "bar", 17 | "encoding": { 18 | "y": {"field": "Miles_per_Gallon", "bin": true}, 19 | "x": {"aggregate": "max", "field": "Horsepower"}, 20 | // "color": {"field": "Origin", "legend": null} 21 | }, 22 | width: 200, 23 | height: 200 24 | }; 25 | 26 | const spec3 = { 27 | "data": {"url": "https://vega.github.io/vega-datasets/data/cars.json"}, 28 | "mark": "bar", 29 | "encoding": { 30 | "y": {"field": "Acceleration", "bin": true}, 31 | "x": {"aggregate": "count"}, 32 | // "color": {"field": "Origin"}, 33 | }, 34 | width: 200, 35 | height: 200 36 | }; 37 | 38 | const spec4 = { 39 | "data": {"url": "https://vega.github.io/vega-datasets/data/cars.json"}, 40 | "mark": "bar", 41 | "encoding": { 42 | "y": {"field": "Displacement", "bin": true}, 43 | "x": {"aggregate": "count"}, 44 | // "color": {"field": "Origin", "legend": null}, 45 | }, 46 | width: 200, 47 | height: 200 48 | }; 49 | 50 | const spec5 = { 51 | "data": {"url": "https://vega.github.io/vega-datasets/data/cars.json"}, 52 | "mark": "point", 53 | "encoding": { 54 | "x": {"field": "Acceleration", "type": "quantitative"}, 55 | "y": {"field": "Displacement", "type": "quantitative"}, 56 | // "size": {"field": "Horsepower", "type": "quantitative"}, 57 | }, 58 | width: 400, 59 | height: 400 60 | }; 61 | 62 | var view1 = new vega.View(vega.parse(vegaLite.compile(spec1).spec), { renderer: 'svg' }); 63 | var view2 = new vega.View(vega.parse(vegaLite.compile(spec2).spec), { renderer: 'svg' }); 64 | var view3 = new vega.View(vega.parse(vegaLite.compile(spec3).spec), { renderer: 'svg' }); 65 | var view4 = new vega.View(vega.parse(vegaLite.compile(spec4).spec), { renderer: 'svg' }); 66 | var view5 = new vega.View(vega.parse(vegaLite.compile(spec5).spec), { renderer: 'svg' }); 67 | 68 | var svg1 = await view1.toSVG(); 69 | var svg2 = await view2.toSVG(); 70 | var svg3 = await view3.toSVG(); 71 | var svg4 = await view4.toSVG(); 72 | var svg5 = await view5.toSVG(); 73 | 74 | document.querySelector("#chart1").innerHTML = svg1; 75 | document.querySelector("#chart1 svg").id = "chart1"; 76 | 77 | // document.querySelector("#chart2").innerHTML = svg2; 78 | // document.querySelector("#chart2 svg").id = "chart2"; 79 | 80 | // document.querySelector("#chart3").innerHTML = svg3; 81 | // document.querySelector("#chart3 svg").id = "chart3"; 82 | 83 | document.querySelector("#chart4").innerHTML = svg4; 84 | document.querySelector("#chart4 svg").id = "chart4"; 85 | 86 | // document.querySelector("#chart5").innerHTML = svg5; 87 | // document.querySelector("#chart5 svg").id = "chart5"; 88 | 89 | divi.hydrate(["#chart1 svg","#chart4 svg"], 90 | { url: "https://vega.github.io/vega-datasets/data/cars.json" }); 91 | } 92 | -------------------------------------------------------------------------------- /examples/visualizations/multi-view/vega-crossfilter.js: -------------------------------------------------------------------------------- 1 | function createVegaCrossfilter() { 2 | const spec1 = { 3 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 4 | "data": { 5 | "url": "https://vega.github.io/vega-datasets/data/flights-2k.json", 6 | "format": {"parse": {"date": "date"}} 7 | }, 8 | "transform": [{"calculate": "hours(datum.date)", "as": "time"}], 9 | "encoding": { 10 | "y": { 11 | "field": "distance", 12 | "bin": {"maxbins": 20} 13 | }, 14 | "x": {"aggregate": "count"} 15 | }, 16 | "mark": "bar", 17 | width: 200, 18 | height: 200 19 | }; 20 | 21 | const spec2 = { 22 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 23 | "data": { 24 | "url": "https://vega.github.io/vega-datasets/data/flights-2k.json", 25 | "format": {"parse": {"date": "date"}} 26 | }, 27 | "transform": [{"calculate": "hours(datum.date)", "as": "time", "filter": {"field": "origin", 28 | "oneOf": ["TUL", "TUS", "LAX", "ABQ", "OKC", "SAN", "LAS"]}}], 29 | "encoding": { 30 | "y": { 31 | "field": "origin", 32 | }, 33 | "x": {"aggregate": "stdev", "field": "delay", "scale": {"domain": [0, 70]}} 34 | }, 35 | "mark": "bar", 36 | width: 200, 37 | height: 200 38 | }; 39 | 40 | // const spec3 = { 41 | // "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 42 | // "data": { 43 | // "url": "https://vega.github.io/vega-datasets/data/flights-2k.json", 44 | // "format": {"parse": {"date": "date"}} 45 | // }, 46 | // "transform": [{"calculate": "hours(datum.date)", "as": "time"}], 47 | // "encoding": { 48 | // "y": { 49 | // "field": "time", 50 | // "bin": {"maxbins": 20} 51 | // }, 52 | // "x": {"aggregate": "count"} 53 | // }, 54 | // "mark": "bar", 55 | // width: 400, 56 | // height: 400 57 | // }; 58 | 59 | const spec4 = { 60 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 61 | "data": { 62 | "url": "https://vega.github.io/vega-datasets/data/flights-2k.json", 63 | "format": {"parse": {"date": "date"}} 64 | }, 65 | "encoding": { 66 | "y": { 67 | "field": "delay", 68 | "bin": {"maxbins": 20} 69 | }, 70 | "x": {"aggregate": "count"} 71 | }, 72 | "mark": "bar", 73 | width: 200, 74 | height: 200 75 | }; 76 | 77 | const spec5 = { 78 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 79 | "data": { 80 | "url": "https://vega.github.io/vega-datasets/data/flights-2k.json", 81 | "format": {"parse": {"date": "date"}} 82 | }, 83 | "transform": [{"calculate": "hours(datum.date)", "as": "time", "filter": {"field": "origin", 84 | "oneOf": ["TUL", "TUS", "LAX", "ABQ", "OKC", "SAN", "LAS"]}}], 85 | "encoding": { 86 | "y": { "title": "Origin", "field": "origin" }, 87 | "x": {"aggregate": "mean", "field": "distance"} 88 | }, 89 | "mark": "bar", 90 | width: 200, 91 | height: 200 92 | }; 93 | 94 | var view1 = new vega.View(vega.parse(vegaLite.compile(spec1).spec), { renderer: 'svg' }); 95 | var view2 = new vega.View(vega.parse(vegaLite.compile(spec4).spec), { renderer: 'svg' }); 96 | var view3 = new vega.View(vega.parse(vegaLite.compile(spec2).spec), { renderer: 'svg' }); 97 | view1.toSVG().then(function(svg1) { 98 | view2.toSVG().then(function(svg2) { 99 | view3.toSVG().then(function(svg3) { 100 | document.querySelector("#chart1").innerHTML = svg1; 101 | document.querySelector("#chart1 svg").id = "chart1"; 102 | 103 | document.querySelector("#chart2").innerHTML = svg2; 104 | document.querySelector("#chart2 svg").id = "chart2"; 105 | 106 | document.querySelector("#chart3").innerHTML = svg3; 107 | document.querySelector("#chart3 svg").id = "chart3"; 108 | 109 | divi.hydrate(["#chart1 svg", "#chart2 svg", "#chart3 svg"], { url: "https://vega.github.io/vega-datasets/data/flights-2k.json" }); 110 | }) 111 | }) 112 | }); 113 | } 114 | -------------------------------------------------------------------------------- /examples/visualizations/multi-view/vega-dual-linking.js: -------------------------------------------------------------------------------- 1 | async function getPlot(id) { 2 | let data = await d3.json('https://vega.github.io/vega-datasets/data/cars.json', d3.autoType); 3 | // const origins = [...new Set(data.map(d => d.Origin))]; 4 | // const incomes = [31,133, 42,000, 17,390]; 5 | 6 | // data = [ 7 | // {Origin: origins[0], Income: incomes[0]}, 8 | // {Origin: origins[1], Income: incomes[1]}, 9 | // {Origin: origins[2], Income: incomes[2]} 10 | // ]; 11 | const p = Plot.plot({ 12 | width: 350, 13 | height: 350, 14 | color: { 15 | legend: false, 16 | style: { marginLeft: 300 }, 17 | scheme: "category10" 18 | }, 19 | x: { 20 | grid: true 21 | }, 22 | y: { 23 | grid: true 24 | }, 25 | marks: [ 26 | Plot.dot(data, {x: "Weight_in_lbs", y: "Acceleration", stroke: "Origin"}) 27 | ] 28 | }); 29 | 30 | return p; 31 | } 32 | 33 | async function createVegaDualLinking() { 34 | const spec1 = { 35 | "data": {"url": "https://vega.github.io/vega-datasets/data/cars.json"}, 36 | "mark": "bar", 37 | "encoding": { 38 | "y": {"field": "Horsepower", "bin": true}, 39 | "x": {"aggregate": "mean", "field": "Acceleration"}, 40 | }, 41 | width: 300, 42 | height: 300 43 | }; 44 | 45 | const spec2 = { 46 | "data": {"url": "https://vega.github.io/vega-datasets/data/cars.json"}, 47 | "mark": "bar", 48 | "encoding": { 49 | "y": {"field": "Acceleration", "bin": true}, 50 | "x": {"aggregate": "max", "field": "Cylinders"}, 51 | }, 52 | width: 300, 53 | height: 300 54 | }; 55 | 56 | const spec3 = { 57 | "data": {"url": "https://vega.github.io/vega-datasets/data/cars.json"}, 58 | "mark": "point", 59 | "encoding": { 60 | "x": {"field": "Horsepower", "type": "quantitative"}, 61 | "y": {"field": "Displacement", "type": "quantitative"}, 62 | "color": {"field": "Origin"}, 63 | }, 64 | width: 300, 65 | height: 300 66 | }; 67 | 68 | var view1 = new vega.View(vega.parse(vegaLite.compile(spec1).spec), { renderer: 'svg' }); 69 | var view2 = new vega.View(vega.parse(vegaLite.compile(spec2).spec), { renderer: 'svg' }); 70 | var view3 = new vega.View(vega.parse(vegaLite.compile(spec3).spec), { renderer: 'svg' }); 71 | 72 | var svg1 = await view1.toSVG(); 73 | var svg2 = await view2.toSVG(); 74 | var svg3 = await view3.toSVG(); 75 | var svg4 = await getPlot('chart4'); 76 | 77 | document.querySelector("#chart1").innerHTML = svg1; 78 | document.querySelector("#chart1 svg").id = "chart1"; 79 | 80 | document.querySelector("#chart2").innerHTML = svg2; 81 | document.querySelector("#chart2 svg").id = "chart2"; 82 | 83 | document.querySelector("#chart3").innerHTML = svg3; 84 | document.querySelector("#chart3 svg").id = "chart3"; 85 | 86 | d3.select("#chart4").node().append(svg4); 87 | d3.select("#chart4 svg").node().id = "chart4"; 88 | d3.select('#chart4 svg').style('max-width', 350) 89 | 90 | divi.hydrate([ "#chart1 svg", "#chart2 svg", "#chart3 svg", "#chart4 svg"], 91 | { url: "https://vega.github.io/vega-datasets/data/cars.json" }); 92 | } 93 | -------------------------------------------------------------------------------- /examples/visualizations/multi-view/vega-lite-weather.js: -------------------------------------------------------------------------------- 1 | async function createVegaMultiView() { 2 | const spec1 = { 3 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 4 | "title": "Seattle Weather, 2012-2015", 5 | "data": { 6 | "url": "https://vega.github.io/vega-datasets/data/seattle-weather.csv" 7 | }, 8 | "encoding": { 9 | "color": { 10 | "title": "Weather", 11 | "field": "weather", 12 | "type": "nominal", 13 | "scale": { 14 | "domain": ["sun", "fog", "drizzle", "rain", "snow"], 15 | "range": ["#e7ba52", "#a7a7a7", "#aec7e8", "#1f77b4", "#9467bd"] 16 | } 17 | }, 18 | "size": { 19 | "title": "Precipitation", 20 | "field": "precipitation", 21 | "scale": {"type": "linear", "domain": [-1, 50]}, 22 | "type": "quantitative" 23 | }, 24 | "x": { 25 | "field": "date", 26 | "timeUnit": "utcyearmonthdate", 27 | "title": "Date", 28 | "axis": {"format": "%b %Y"} 29 | }, 30 | "y": { 31 | "title": "Maximum Daily Temperature (C)", 32 | "field": "temp_max", 33 | "scale": {"domain": [-5, 40]}, 34 | "type": "quantitative" 35 | } 36 | }, 37 | "width": 700, 38 | "height": 400, 39 | "mark": "point" 40 | } 41 | 42 | const spec2 = { 43 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 44 | "title": "Seattle Weather, 2012-2015", 45 | "data": { 46 | "url": "https://vega.github.io/vega-datasets/data/seattle-weather.csv" 47 | }, 48 | "encoding": { 49 | "color": { 50 | "field": "weather", 51 | "scale": { 52 | "domain": ["sun", "fog", "drizzle", "rain", "snow"], 53 | "range": ["#e7ba52", "#a7a7a7", "#aec7e8", "#1f77b4", "#9467bd"] 54 | } 55 | }, 56 | "x": {"aggregate": "max", "field": "wind"}, 57 | "y": {"title": "Weather", "field": "weather"} 58 | }, 59 | "width": 700, 60 | "mark": "bar" 61 | } 62 | 63 | const spec3 = { 64 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 65 | "title": "Seattle Weather, 2012-2015", 66 | "data": { 67 | "url": "https://vega.github.io/vega-datasets/data/seattle-weather.csv" 68 | }, 69 | "encoding": { 70 | "x": {"aggregate": "max", "field": "wind"}, 71 | "y": {"field": "precipitation", "bin": true} 72 | }, 73 | "width": 700, 74 | "mark": "bar" 75 | } 76 | 77 | var view1 = new vega.View(vega.parse(vegaLite.compile(spec1).spec), { renderer: 'svg' }); 78 | var view2 = new vega.View(vega.parse(vegaLite.compile(spec2).spec), { renderer: 'svg' }); 79 | var view3 = new vega.View(vega.parse(vegaLite.compile(spec3).spec), { renderer: 'svg' }); 80 | 81 | var svg3 = await view3.toSVG(); 82 | 83 | view1.toSVG().then(function(svg1) { 84 | view2.toSVG().then(function(svg2) { 85 | document.querySelector("#chart1").innerHTML = svg1; 86 | document.querySelector("#chart1 svg").id = "chart1"; 87 | document.querySelector("#chart2").innerHTML = svg2; 88 | document.querySelector("#chart2 svg").id = "chart2"; 89 | // document.querySelector("#chart3").innerHTML = svg3; 90 | // document.querySelector("#chart3 svg").id = "chart3"; 91 | divi.hydrate(["#chart1 svg", "#chart2 svg"], { url: "https://vega.github.io/vega-datasets/data/seattle-weather.csv" }); 92 | }) 93 | }); 94 | } 95 | -------------------------------------------------------------------------------- /examples/visualizations/multi-view/vega-matplotlib.js: -------------------------------------------------------------------------------- 1 | async function createVegaMatplotlib() { 2 | const spec1 = { 3 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 4 | "title": "Seattle Weather, 2012-2015", 5 | "data": { 6 | "url": "https://vega.github.io/vega-datasets/data/seattle-weather.csv" 7 | }, 8 | "encoding": { 9 | "color": { 10 | "title": "Weather", 11 | "field": "weather", 12 | "type": "nominal", 13 | "scale": { 14 | "domain": ["sun", "fog", "drizzle", "rain", "snow"], 15 | "range": ["#e7ba52", "#a7a7a7", "#aec7e8", "#1f77b4", "#9467bd"] 16 | } 17 | }, 18 | "size": { 19 | "title": "Precipitation", 20 | "field": "precipitation", 21 | "scale": {"type": "linear", "domain": [-1, 50]}, 22 | "type": "quantitative" 23 | }, 24 | "x": { 25 | "field": "date", 26 | "timeUnit": "utcyearmonthdate", 27 | "title": "Date", 28 | "axis": {"format": "%b %Y"} 29 | }, 30 | "y": { 31 | "title": "Maximum Daily Temperature (C)", 32 | "field": "temp_max", 33 | "scale": {"domain": [-5, 40]}, 34 | "type": "quantitative" 35 | } 36 | }, 37 | "width": 700, 38 | "height": 400, 39 | "mark": "point" 40 | } 41 | 42 | var view1 = new vega.View(vega.parse(vegaLite.compile(spec1).spec), { renderer: 'svg' }); 43 | var svg2 = await fetch('/examples/visualizations/matplotlib/bars.svg'); 44 | svg2 = await svg2.text(); 45 | 46 | view1.toSVG().then(function(svg1) { 47 | document.querySelector("#chart1").innerHTML = svg1; 48 | document.querySelector("#chart1 svg").id = "chart1"; 49 | document.querySelector("#chart2").innerHTML = svg2; 50 | document.querySelector("#chart2 svg").id = "chart2"; 51 | divi.hydrate(["#chart1 svg", "#chart2 svg"], { url: "https://vega.github.io/vega-datasets/data/seattle-weather.csv" }); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /examples/visualizations/observable-plot/bar-chart.js: -------------------------------------------------------------------------------- 1 | async function createBarChart() { 2 | let data = await d3.csv('https://raw.githubusercontent.com/observablehq/plot/main/test/data/alphabet.csv', d3.autoType); 3 | 4 | const p = Plot.plot({ 5 | width: 640, 6 | height: 400, 7 | y: { 8 | grid: true 9 | }, 10 | marks: [ 11 | Plot.barY(data, {x: "letter", y: "frequency"}) 12 | ] 13 | }); 14 | 15 | d3.select(p).attr('id', 'chart1'); 16 | d3.select('#container').node().append(p); 17 | divi.hydrate("#chart1"); 18 | } 19 | -------------------------------------------------------------------------------- /examples/visualizations/observable-plot/facets.js: -------------------------------------------------------------------------------- 1 | async function createFacets() { 2 | let data = await d3.json('https://vega.github.io/vega-datasets/data/cars.json'); 3 | 4 | const p1 = Plot.plot({ 5 | width: 300, 6 | height: 300, 7 | y: { 8 | grid: true 9 | }, 10 | color: { 11 | legend: false 12 | }, 13 | marks: [ 14 | Plot.dot(data, {x: "Acceleration", y: "Horsepower", stroke: "Origin"}) 15 | ] 16 | }); 17 | 18 | const p2 = Plot.plot({ 19 | width: 300, 20 | height: 300, 21 | y: { 22 | grid: true 23 | }, 24 | color: { 25 | legend: false 26 | }, 27 | marks: [ 28 | Plot.dot(data, {x: "Acceleration", y: "Displacement", stroke: "Origin"}) 29 | ] 30 | }); 31 | const p3 = Plot.plot({ 32 | width: 300, 33 | height: 300, 34 | y: { 35 | grid: true 36 | }, 37 | color: { 38 | legend: false 39 | }, 40 | marks: [ 41 | Plot.dot(data, {x: "Weight_in_lbs", y: "Horsepower", stroke: "Origin"}) 42 | ] 43 | }); 44 | const p4 = Plot.plot({ 45 | width: 300, 46 | height: 300, 47 | color: { 48 | legend: false 49 | }, 50 | y: { 51 | grid: true 52 | }, 53 | marks: [ 54 | Plot.dot(data, {x: "Cylinders", y: "Acceleration", stroke: "Origin"}) 55 | ] 56 | }); 57 | 58 | d3.select(p1).attr('id', 'chart1'); 59 | d3.select('#container').node().append(p1); 60 | 61 | d3.select(p2).attr('id', 'chart2'); 62 | d3.select('#container').node().append(p2); 63 | 64 | d3.select(p3).attr('id', 'chart3'); 65 | d3.select('#container').node().append(p3); 66 | 67 | d3.select(p4).attr('id', 'chart4'); 68 | d3.select('#container').node().append(p4); 69 | 70 | divi.hydrate(['#chart1', '#chart2', '#chart3', "#chart4"], { url: 'https://vega.github.io/vega-datasets/data/cars.json' }); 71 | } 72 | -------------------------------------------------------------------------------- /examples/visualizations/observable-plot/scatter-plot.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | // eslint-disable-next-line no-unused-vars 3 | async function createScatterPlot() { 4 | let data = await d3.csv('/examples/data/penguins.csv', d3.autoType); 5 | data = data.filter(d => d.sex != null); 6 | 7 | const p = Plot.plot({ 8 | inset: 8, 9 | grid: true, 10 | color: { 11 | legend: false, 12 | style: { marginLeft: 500 } 13 | }, 14 | marks: [ 15 | Plot.dot(data, { x: 'flipper_length_mm', y: 'body_mass_g', stroke: 'sex' }), 16 | Plot.axisY({ label: '↑ body_mass_g', marginLeft: 50 }) 17 | ] 18 | }); 19 | 20 | d3.select(p).attr('id', 'chart1'); 21 | return p; 22 | } 23 | -------------------------------------------------------------------------------- /examples/visualizations/vega-lite/bar-chart.js: -------------------------------------------------------------------------------- 1 | function createBarChart() { 2 | const spec = { 3 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 4 | "title": "Seattle Weather, 2012-2015", 5 | "data": { 6 | "url": "https://vega.github.io/vega-datasets/data/seattle-weather.csv" 7 | }, 8 | "encoding": { 9 | "color": { 10 | "field": "weather", 11 | "scale": { 12 | "domain": ["sun", "fog", "drizzle", "rain", "snow"], 13 | "range": ["#e7ba52", "#a7a7a7", "#aec7e8", "#1f77b4", "#9467bd"] 14 | } 15 | }, 16 | "x": {"aggregate": "count"}, 17 | "y": {"title": "Weather", "field": "weather"} 18 | }, 19 | "width": 800, 20 | "mark": "bar" 21 | } 22 | var view = new vega.View(vega.parse(vegaLite.compile(spec).spec), { renderer: 'svg' }); 23 | view.toSVG().then(function(svg) { 24 | document.querySelector("#container").innerHTML = svg; 25 | document.querySelector("#container svg").id = "chart"; 26 | divi.hydrate("#chart"); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /examples/visualizations/vega-lite/line-chart.js: -------------------------------------------------------------------------------- 1 | function createLineChart() { 2 | const spec = { 3 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 4 | "description": "Stock prices of 5 Tech Companies over Time.", 5 | "data": {"url": "https://vega.github.io/vega-datasets/data/stocks.csv"}, 6 | "mark": "line", 7 | "height": 450, 8 | "width": 450, 9 | "encoding": { 10 | "x": {"field": "date", "type": "temporal"}, 11 | "y": {"field": "price", "type": "quantitative"}, 12 | "color": {"field": "symbol", "type": "nominal"} 13 | } 14 | } 15 | 16 | var view = new vega.View(vega.parse(vegaLite.compile(spec).spec), { renderer: 'svg' }); 17 | view.toSVG().then(function(svg) { 18 | document.querySelector("#container1").innerHTML = svg; 19 | document.querySelector("#container1 svg").id = "chart"; 20 | // divi.hydrate("#chart"); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /examples/visualizations/vega-lite/scatter-plot-1.js: -------------------------------------------------------------------------------- 1 | function createScatterPlot() { 2 | const spec = { 3 | "$schema": "https://vega.github.io/schema/vega/v5.json", 4 | "description": "A basic scatter plot example depicting automobile statistics.", 5 | "width": 450, 6 | "height": 450, 7 | "padding": 5, 8 | 9 | "data": [ 10 | { 11 | "name": "source", 12 | "url": "https://vega.github.io/vega-datasets/data/cars.json", 13 | "transform": [ 14 | { 15 | "type": "filter", 16 | "expr": "datum['Horsepower'] != null && datum['Miles_per_Gallon'] != null && datum['Acceleration'] != null" 17 | } 18 | ] 19 | } 20 | ], 21 | 22 | "scales": [ 23 | { 24 | "name": "x", 25 | "type": "linear", 26 | "round": true, 27 | "nice": true, 28 | "zero": true, 29 | "domain": {"data": "source", "field": "Horsepower"}, 30 | "range": "width" 31 | }, 32 | { 33 | "name": "y", 34 | "type": "linear", 35 | "round": true, 36 | "nice": true, 37 | "zero": true, 38 | "domain": {"data": "source", "field": "Miles_per_Gallon"}, 39 | "range": "height" 40 | }, 41 | { 42 | "name": "size", 43 | "type": "linear", 44 | "round": true, 45 | "nice": false, 46 | "zero": true, 47 | "domain": {"data": "source", "field": "Acceleration"}, 48 | "range": [4,361] 49 | } 50 | ], 51 | 52 | "axes": [ 53 | { 54 | "scale": "x", 55 | "grid": true, 56 | "domain": false, 57 | "orient": "bottom", 58 | "tickCount": 5, 59 | "title": "Horsepower" 60 | }, 61 | { 62 | "scale": "y", 63 | "grid": true, 64 | "domain": false, 65 | "orient": "left", 66 | "titlePadding": 5, 67 | "title": "Miles_per_Gallon" 68 | } 69 | ], 70 | 71 | "legends": [ 72 | { 73 | "size": "size", 74 | "title": "Acceleration", 75 | "format": "s", 76 | "symbolStrokeColor": "#4682b4", 77 | "symbolStrokeWidth": 2, 78 | "symbolOpacity": 1, 79 | "symbolType": "circle" 80 | } 81 | ], 82 | 83 | "marks": [ 84 | { 85 | "name": "marks", 86 | "type": "symbol", 87 | "from": {"data": "source"}, 88 | "encode": { 89 | "update": { 90 | "x": {"scale": "x", "field": "Horsepower"}, 91 | "y": {"scale": "y", "field": "Miles_per_Gallon"}, 92 | "size": {"scale": "size", "field": "Acceleration"}, 93 | "shape": {"value": "circle"}, 94 | "strokeWidth": {"value": 2}, 95 | "opacity": {"value": 1}, 96 | "stroke": {"value": "#4682b4"}, 97 | "fill": {"value": "transparent"} 98 | } 99 | } 100 | } 101 | ] 102 | } 103 | 104 | var view = new vega.View(vega.parse(spec), { renderer: 'svg' }); 105 | view.toSVG().then(function(svg) { 106 | document.querySelector("#container").innerHTML = svg; 107 | document.querySelector("#container svg").id = "chart"; 108 | divi.hydrate("#chart"); 109 | }); 110 | } 111 | -------------------------------------------------------------------------------- /examples/visualizations/vega-lite/scatter-plot-2.js: -------------------------------------------------------------------------------- 1 | function createScatterPlot() { 2 | const spec = { 3 | "$schema": "https://vega.github.io/schema/vega-lite/v5.json", 4 | "title": "Seattle Weather, 2012-2015", 5 | "data": { 6 | "url": "https://vega.github.io/vega-datasets/data/seattle-weather.csv" 7 | }, 8 | "encoding": { 9 | "color": { 10 | "title": "Weather", 11 | "field": "weather", 12 | "type": "nominal", 13 | "scale": { 14 | "domain": ["sun", "fog", "drizzle", "rain", "snow"], 15 | "range": ["#e7ba52", "#a7a7a7", "#aec7e8", "#1f77b4", "#9467bd"] 16 | } 17 | }, 18 | "size": { 19 | "title": "Precipitation", 20 | "field": "precipitation", 21 | "scale": {"domain": [-1, 50]}, 22 | "type": "quantitative" 23 | }, 24 | "x": { 25 | "field": "date", 26 | "timeUnit": "monthdate", 27 | "title": "Date", 28 | "axis": {"format": "%m"} 29 | }, 30 | "y": { 31 | "title": "Maximum Daily Temperature (C)", 32 | "field": "temp_max", 33 | "scale": {"domain": [-5, 40]}, 34 | "type": "quantitative" 35 | } 36 | }, 37 | "width": 800, 38 | "height": 500, 39 | "mark": "point" 40 | } 41 | var view = new vega.View(vega.parse(vegaLite.compile(spec).spec), { renderer: 'svg' }); 42 | view.toSVG().then(function(svg) { 43 | document.querySelector("#container").innerHTML = svg; 44 | document.querySelector("#container svg").id = "chart"; 45 | divi.hydrate("#chart"); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@uwdata/divi", 3 | "version": "0.0.0", 4 | "description": "Dynamic interaction for SVG visualizations", 5 | "main": "src/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/uwdata/DIVI.git" 9 | }, 10 | "type": "module", 11 | "author": "Luke Snyder (https://luke-s-snyder.github.io)", 12 | "license": "BSD-3-Clause", 13 | "scripts": { 14 | "docs:dev": "vitepress dev docs", 15 | "docs:build": "vitepress build docs", 16 | "docs:preview": "vitepress preview docs", 17 | "prebuild": "rimraf dist && mkdir dist", 18 | "build": "rollup -c", 19 | "lint": "eslint src test --ext .js", 20 | "test": "mocha 'test/node/**/*-test.js'", 21 | "prepublishOnly": "npm run test && npm run lint && npm run build", 22 | "serve": "wds --watch" 23 | }, 24 | "devDependencies": { 25 | "@rollup/plugin-commonjs": "^25.0.7", 26 | "@rollup/plugin-json": "^6.1.0", 27 | "@rollup/plugin-terser": "^0.4.4", 28 | "@web/dev-server": "^0.4.6", 29 | "eslint": "^8.41.0", 30 | "eslint-config-standard": "^17.1.0", 31 | "eslint-plugin-import": "^2.27.5", 32 | "eslint-plugin-n": "^16.0.0", 33 | "eslint-plugin-promise": "^6.1.1", 34 | "mocha": "^10.4.0", 35 | "rimraf": "^5.0.5", 36 | "rollup": "^4.16.4", 37 | "rollup-plugin-bundle-size": "^1.0.3", 38 | "rollup-plugin-postcss": "^4.0.2", 39 | "rollup-plugin-svg": "^2.0.0", 40 | "vitepress": "^1.1.4" 41 | }, 42 | "dependencies": { 43 | "arquero": "^5.4.0", 44 | "bootstrap": "^5.2.3", 45 | "d3-array": "^3.2.3", 46 | "d3-dispatch": "^3.0.1", 47 | "d3-drag": "^3.0.0", 48 | "d3-format": "^3.1.0", 49 | "d3-path": "^3.1.0", 50 | "d3-scale": "^4.0.2", 51 | "d3-selection": "^3.0.0", 52 | "d3-svg-annotation": "^2.5.1", 53 | "d3-time-format": "^4.1.0", 54 | "d3-transition": "^3.0.1", 55 | "svg-path-parser": "^1.1.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |