├── .jshintignore ├── src ├── components │ ├── schemalist │ │ ├── schemalist.scss │ │ ├── schemalist.html │ │ ├── schemalist.js │ │ └── schemalist.spec.js │ ├── propertyeditor │ │ ├── propertyeditor.scss │ │ ├── propertyeditor.js │ │ ├── propertyeditor.html │ │ └── propertyeditor.spec.js │ ├── schemalistitem │ │ ├── schemalistitem.scss │ │ ├── schemalistitem.html │ │ ├── schemalistitem.js │ │ └── schemalistitem.spec.js │ ├── nullfilterdirective │ │ ├── nullfilterdirective.scss │ │ ├── nullfilterdirective.html │ │ ├── nullfilterdirective.js │ │ └── nullfilterdirective.spec.js │ ├── shelves │ │ ├── shelves.scss │ │ ├── shelves.js │ │ ├── shelves.spec.js │ │ └── shelves.html │ ├── configurationeditor │ │ ├── configurationeditor.html │ │ ├── configurationeditor.js │ │ └── configurationeditor.spec.js │ ├── alertmessages │ │ ├── alertmessages.html │ │ ├── alertmessages.js │ │ └── alertmessages.spec.js │ ├── functionselect │ │ ├── functionselect.html │ │ ├── functionselect.spec.js │ │ └── functionselect.js │ ├── vgSpecEditor │ │ ├── vgSpecEditor.js │ │ ├── vgSpecEditor.html │ │ └── vgSpecEditor.spec.js │ ├── jsoninput │ │ ├── jsoninput.js │ │ └── jsoninput.spec.js │ ├── vlSpecEditor │ │ ├── vlSpecEditor.js │ │ ├── vlSpecEditor.html │ │ └── vlSpecEditor.spec.js │ ├── lyraexport │ │ ├── lyraexport.spec.js │ │ └── lyraexport.js │ └── fielddefeditor │ │ ├── fielddefeditor.spec.js │ │ ├── fielddefeditor.scss │ │ ├── fielddefeditor.js │ │ └── fielddefeditor.html ├── favicon.ico ├── assets │ ├── images │ │ ├── field_geo.png │ │ ├── field_text.png │ │ ├── field_time.png │ │ └── field_number.png │ └── normalize.scss ├── app │ ├── main │ │ ├── main.controller.spec.js │ │ ├── main.controller.js │ │ └── main.html │ ├── pills │ │ ├── pills.service.spec.js │ │ └── pills.service.js │ ├── index.scss │ ├── spec │ │ ├── spec.spec.js │ │ └── spec.service.js │ └── index.js ├── data │ ├── crimea.json │ ├── burtin.json │ ├── driving.json │ ├── barley.json │ ├── iris.json │ └── population.json └── index.html ├── .gitignore ├── .bowerrc ├── .travis.yml ├── .bower-postinstall ├── gulpfile.js ├── deploy.sh ├── gulp ├── lint.js ├── unit-tests.js ├── bumpver.js ├── watch.js ├── e2e-tests.js ├── inject.js ├── styles.js ├── server.js ├── proxy.js ├── build.js └── gen.js ├── .jshintrc ├── e2e ├── main.po.js └── main.spec.js ├── protractor.conf.js ├── bower.json ├── LICENSE ├── .yo-rc.json ├── package.json ├── karma.conf.js └── README.md /.jshintignore: -------------------------------------------------------------------------------- 1 | app/scripts/vendor/*.js -------------------------------------------------------------------------------- /src/components/schemalist/schemalist.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/propertyeditor/propertyeditor.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/schemalistitem/schemalistitem.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/nullfilterdirective/nullfilterdirective.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/shelves/shelves.scss: -------------------------------------------------------------------------------- 1 | .shelf-pane { 2 | margin-bottom: 20px; 3 | } -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sandbox/polestar/master/src/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | .sass-cache/ 4 | .tmp/ 5 | dist/ 6 | test.log -------------------------------------------------------------------------------- /src/assets/images/field_geo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sandbox/polestar/master/src/assets/images/field_geo.png -------------------------------------------------------------------------------- /src/assets/images/field_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sandbox/polestar/master/src/assets/images/field_text.png -------------------------------------------------------------------------------- /src/assets/images/field_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sandbox/polestar/master/src/assets/images/field_time.png -------------------------------------------------------------------------------- /src/assets/images/field_number.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sandbox/polestar/master/src/assets/images/field_number.png -------------------------------------------------------------------------------- /src/components/configurationeditor/configurationeditor.html: -------------------------------------------------------------------------------- 1 |
2 |
{{ Config.config | compactJSON }}
3 |
4 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "scripts": { 4 | "postinstall": "./.bower-postinstall" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_install: 5 | - npm install -g bower 6 | - npm install 7 | - bower install 8 | -------------------------------------------------------------------------------- /.bower-postinstall: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cp bower_components/angular-tooltips/dist/angular-tooltips.min.css bower_components/angular-tooltips/dist/_angular-tooltips.scss -------------------------------------------------------------------------------- /src/components/nullfilterdirective/nullfilterdirective.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/schemalist/schemalist.html: -------------------------------------------------------------------------------- 1 |
2 | 5 | 6 |
7 | -------------------------------------------------------------------------------- /src/app/main/main.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('controllers', function() { 4 | var scope; 5 | 6 | beforeEach(module('polestar')); 7 | 8 | beforeEach(inject(function($rootScope) { 9 | scope = $rootScope.$new(); 10 | })); 11 | }); 12 | -------------------------------------------------------------------------------- /src/components/alertmessages/alertmessages.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{ alert.msg }} 4 | × 5 |
6 |
7 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | 5 | gulp.paths = { 6 | src: 'src', 7 | dist: 'dist', 8 | tmp: '.tmp', 9 | e2e: 'e2e' 10 | }; 11 | 12 | require('require-dir')('./gulp'); 13 | 14 | gulp.task('default', ['clean'], function () { 15 | gulp.start('build'); 16 | }); 17 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | gitsha=$(git rev-parse HEAD) 4 | 5 | git clone git@github.com:uwdata/polestar.git gh-pages 6 | cd gh-pages 7 | git checkout gh-pages 8 | cd .. 9 | gulp 10 | rm -rf dist/.git 11 | mv gh-pages/.git dist 12 | rm -rf gh-pages 13 | cd dist 14 | git add . 15 | git commit -am "release $gitsha" 16 | git push 17 | cd .. -------------------------------------------------------------------------------- /src/components/functionselect/functionselect.html: -------------------------------------------------------------------------------- 1 |
2 |

Functions

3 | 7 |
-------------------------------------------------------------------------------- /gulp/lint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var paths = gulp.paths; 5 | var $ = require('gulp-load-plugins')(); 6 | 7 | gulp.task('jshint', function() { 8 | return gulp.src([ 9 | paths.src + '/**/*.js', 10 | '!'+ paths.src + '/vendor/*.js' 11 | ]) 12 | .pipe($.jshint()) 13 | .pipe($.jshint.reporter('jshint-stylish')); 14 | }); 15 | -------------------------------------------------------------------------------- /src/components/alertmessages/alertmessages.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .directive('alertMessages', function(Alerts) { 5 | return { 6 | templateUrl: 'components/alertmessages/alertmessages.html', 7 | restrict: 'E', 8 | scope: {}, 9 | link: function(scope /*, element, attrs*/) { 10 | scope.Alerts = Alerts; 11 | } 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /src/components/vgSpecEditor/vgSpecEditor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .directive('vgSpecEditor', function(Spec) { 5 | return { 6 | templateUrl: 'components/vgSpecEditor/vgSpecEditor.html', 7 | restrict: 'E', 8 | scope: {}, 9 | link: function postLink(scope /*, element, attrs*/) { 10 | scope.Spec = Spec; 11 | } 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /src/components/configurationeditor/configurationeditor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .directive('configurationEditor', function() { 5 | return { 6 | templateUrl: 'components/configurationeditor/configurationeditor.html', 7 | restrict: 'E', 8 | scope: {}, 9 | controller: function($scope, Config) { 10 | $scope.Config = Config; 11 | } 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /src/components/schemalistitem/schemalistitem.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef": true, 3 | "unused": true, 4 | "eqnull": true, 5 | "freeze": true, 6 | "noarg": true, 7 | "node": true, 8 | "browser": true, 9 | "mocha": true, 10 | "globalstrict":true, 11 | "indent": 2, 12 | "quotmark": "single", 13 | "undef": true, 14 | "globals": { 15 | "angular": false, 16 | // Angular Mocks 17 | "inject": false, 18 | // assert 19 | "expect": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/components/vgSpecEditor/vgSpecEditor.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | Copy 5 | 6 |
7 |

Vega Specification

8 |
9 | 10 |
-------------------------------------------------------------------------------- /src/components/schemalist/schemalist.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .directive('schemaList', function(Dataset) { 5 | return { 6 | templateUrl: 'components/schemalist/schemalist.html', 7 | restrict: 'E', 8 | scope: {}, 9 | replace: true, 10 | // link: function postLink(scope, element /*, attrs*/) { 11 | // }, 12 | controller: function($scope) { 13 | $scope.Dataset = Dataset; 14 | } 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /src/components/jsoninput/jsoninput.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .directive('jsonInput', function() { 5 | return { 6 | restrict: 'A', 7 | require: 'ngModel', 8 | scope: {}, 9 | link: function(scope, element, attrs, modelCtrl) { 10 | var format = function(inputValue) { 11 | return JSON.stringify(inputValue, null, ' ', 80); 12 | }; 13 | modelCtrl.$formatters.push(format); 14 | } 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /gulp/unit-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var karma = require('karma').server; 5 | 6 | function runTests (singleRun, done) { 7 | karma.start({ 8 | configFile: __dirname + '/../karma.conf.js', 9 | singleRun: singleRun 10 | }, function() { done(); }); 11 | } 12 | 13 | gulp.task('test', ['partials'], function (done) { 14 | runTests(true /* singleRun */, done); 15 | }); 16 | 17 | gulp.task('test:auto', ['partials'], function (done) { 18 | runTests(false /* singleRun */, done); 19 | }); 20 | -------------------------------------------------------------------------------- /src/app/pills/pills.service.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global vl:true */ 4 | /* jshint expr:true */ 5 | 6 | describe('Service: Pills', function () { 7 | 8 | // load the service's module 9 | beforeEach(module('polestar', function($provide) { 10 | $provide.constant('vl', vl); 11 | })); 12 | 13 | // instantiate service 14 | var Pills; 15 | beforeEach(inject(function (_Pills_) { 16 | Pills = _Pills_; 17 | })); 18 | 19 | it('should do something', function () { 20 | expect(Pills).to.be.ok; 21 | }); 22 | 23 | }); -------------------------------------------------------------------------------- /e2e/main.po.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file uses the Page Object pattern to define the main page for tests 3 | * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ 4 | */ 5 | 6 | 'use strict'; 7 | 8 | var MainPage = function() { 9 | this.jumbEl = element(by.css('.jumbotron')); 10 | this.h1El = this.jumbEl.element(by.css('h1')); 11 | this.imgEl = this.jumbEl.element(by.css('img')); 12 | this.thumbnailEls = element(by.css('body')).all(by.repeater('awesomeThing in awesomeThings')); 13 | }; 14 | 15 | module.exports = new MainPage(); 16 | -------------------------------------------------------------------------------- /src/components/vlSpecEditor/vlSpecEditor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .directive('vlSpecEditor', function(Spec) { 5 | return { 6 | templateUrl: 'components/vlSpecEditor/vlSpecEditor.html', 7 | restrict: 'E', 8 | scope: {}, 9 | link: function postLink(scope /*, element, attrs*/) { 10 | scope.Spec = Spec; 11 | 12 | scope.parseShorthand = Spec.parseShorthand; 13 | scope.parseVegalite = function(specJSON) { 14 | Spec.parseSpec(JSON.parse(specJSON)); 15 | }; 16 | } 17 | }; 18 | }); 19 | -------------------------------------------------------------------------------- /gulp/bumpver.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var $ = require('gulp-load-plugins')(); 5 | 6 | 7 | function inc(importance) { 8 | // get all the files to bump version in 9 | return gulp.src(['./package.json', './bower.json']) 10 | // bump the version number in those files 11 | .pipe($.bump({type: importance})) 12 | // save it back to filesystem 13 | .pipe(gulp.dest('./')); 14 | } 15 | 16 | gulp.task('patch', function() { return inc('patch'); }); 17 | gulp.task('feature', function() { return inc('minor'); }); 18 | gulp.task('release', function() { return inc('major'); }); -------------------------------------------------------------------------------- /src/components/propertyeditor/propertyeditor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc directive 5 | * @name vega-lite-ui.directive:propertyEditor 6 | * @description 7 | * # propertyEditor 8 | */ 9 | angular.module('polestar') 10 | .directive('propertyEditor', function () { 11 | return { 12 | templateUrl: 'components/propertyeditor/propertyeditor.html', 13 | restrict: 'E', 14 | scope: { 15 | id: '=', 16 | type: '=', 17 | enum: '=', 18 | propName: '=', 19 | group: '=' 20 | }, 21 | link: function postLink(/*scope, element, attrs*/) { 22 | } 23 | }; 24 | }); -------------------------------------------------------------------------------- /gulp/watch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | 5 | var paths = gulp.paths; 6 | 7 | gulp.task('watch', ['inject'], function () { 8 | gulp.watch([ 9 | paths.src + '/*.html', 10 | paths.src + '/{app,components}/**/*.scss', 11 | paths.src + '/{app,components}/**/*.js', 12 | paths.src + '/assets/*.scss', 13 | paths.src + '/bower_components/vega-lite/vega-lite.js', 14 | paths.src + '/bower_components/datalib/datalib.js', 15 | paths.src + '/bower_components/vega-lite-ui/vlui.js', 16 | paths.src + '/bower_components/vega-lite-ui/vlui.scss', 17 | 'bower.json' 18 | ], ['inject', 'jshint']); 19 | }); 20 | -------------------------------------------------------------------------------- /e2e/main.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('The main view', function () { 4 | var page; 5 | 6 | beforeEach(function () { 7 | browser.get('http://localhost:3000/index.html'); 8 | page = require('./main.po'); 9 | }); 10 | 11 | it('should include jumbotron with correct data', function() { 12 | expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); 13 | expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); 14 | expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); 15 | }); 16 | 17 | it('list more than 5 awesome things', function () { 18 | expect(page.thumbnailEls.count()).toBeGreaterThan(5); 19 | }); 20 | 21 | }); 22 | -------------------------------------------------------------------------------- /src/components/jsoninput/jsoninput.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Directive: jsonInput', function() { 4 | 5 | // load the directive's module 6 | beforeEach(module('polestar')); 7 | 8 | var element, 9 | scope; 10 | 11 | beforeEach(inject(function($rootScope) { 12 | scope = $rootScope.$new(); 13 | scope.foo = {foo: 'bar'}; 14 | })); 15 | 16 | it('should make hidden element visible', inject(function($compile) { 17 | element = angular.element(''); 18 | element = $compile(element)(scope); 19 | scope.$digest(); 20 | 21 | expect(element.val()).to.eql('{"foo": "bar"}'); 22 | })); 23 | }); 24 | -------------------------------------------------------------------------------- /src/components/nullfilterdirective/nullfilterdirective.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc directive 5 | * @name polestar.directive:nullFilterDirective 6 | * @description 7 | * # nullFilterDirective 8 | */ 9 | angular.module('polestar') 10 | .directive('nullFilterDirective', function (Spec) { 11 | return { 12 | templateUrl: 'components/nullfilterdirective/nullfilterdirective.html', 13 | restrict: 'E', 14 | scope: {}, 15 | link: function postLink(scope, element, attrs) { 16 | // jshint unused:false 17 | scope.Spec = Spec; 18 | 19 | scope.updateFilter = function() { 20 | Spec.update(); 21 | }; 22 | } 23 | }; 24 | }); -------------------------------------------------------------------------------- /src/components/propertyeditor/propertyeditor.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 |
8 | -------------------------------------------------------------------------------- /src/components/lyraexport/lyraexport.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Directive: lyraExport', function() { 4 | 5 | // load the directive's module 6 | beforeEach(module('polestar')); 7 | 8 | beforeEach(module('polestar', function($provide) { 9 | var mock = { 10 | vgSpec: {} 11 | }; 12 | $provide.value('Spec', mock); 13 | })); 14 | 15 | var element, 16 | scope; 17 | 18 | beforeEach(inject(function($rootScope) { 19 | scope = $rootScope.$new(); 20 | })); 21 | 22 | it('should make hidden element visible', inject(function($compile) { 23 | element = angular.element(''); 24 | element = $compile(element)(scope); 25 | expect(element.text()).to.eql('Export to lyra'); 26 | })); 27 | }); 28 | -------------------------------------------------------------------------------- /src/components/nullfilterdirective/nullfilterdirective.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Directive: nullFilterDirective', function () { 4 | 5 | // load the directive's module 6 | beforeEach(module('polestar')); 7 | 8 | var element, 9 | scope; 10 | 11 | beforeEach(module('polestar', function($provide) { 12 | $provide.value('Dataset', {}); 13 | $provide.value('Spec', {}); 14 | })); 15 | 16 | beforeEach(inject(function ($rootScope) { 17 | scope = $rootScope.$new(); 18 | })); 19 | 20 | it('should make hidden element visible', inject(function ($compile) { 21 | element = angular.element(''); 22 | element = $compile(element)(scope); 23 | expect(element.length).to.eql(1); 24 | })); 25 | }); -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var paths = require('./.yo-rc.json')['generator-gulp-angular'].props.paths; 4 | 5 | // An example configuration file. 6 | exports.config = { 7 | // The address of a running selenium server. 8 | //seleniumAddress: 'http://localhost:4444/wd/hub', 9 | //seleniumServerJar: deprecated, this should be set on node_modules/protractor/config.json 10 | 11 | // Capabilities to be passed to the webdriver instance. 12 | capabilities: { 13 | 'browserName': 'chrome' 14 | }, 15 | 16 | // Spec patterns are relative to the current working directly when 17 | // protractor is called. 18 | specs: [paths.e2e + '/**/*.js'], 19 | 20 | // Options to be passed to Jasmine-node. 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/components/schemalistitem/schemalistitem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc directive 5 | * @name polestar.directive:schemaListItem 6 | * @description 7 | * # schemaListItem 8 | */ 9 | angular.module('polestar') 10 | .directive('schemaListItem', function (Pills) { 11 | return { 12 | templateUrl: 'components/schemalistitem/schemalistitem.html', 13 | restrict: 'E', 14 | replace: false, 15 | scope: { 16 | field:'=' 17 | }, 18 | link: function postLink(scope) { 19 | scope.getSchemaPill = Pills.getSchemaPill; 20 | 21 | scope.fieldDragStart = function() { 22 | scope.pill = Pills.getSchemaPill(scope.field); 23 | Pills.dragStart(scope.pill, null); 24 | }; 25 | 26 | scope.fieldDragStop = Pills.dragStop; 27 | } 28 | }; 29 | }); -------------------------------------------------------------------------------- /src/components/alertmessages/alertmessages.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Directive: alertMessages', function() { 4 | 5 | // load the directive's module 6 | beforeEach(module('polestar')); 7 | 8 | var element, 9 | scope; 10 | 11 | beforeEach(module('polestar', function($provide) { 12 | var mock = { 13 | alerts: [ 14 | {name: 'foo'}, 15 | {name: 'bar'} 16 | ] 17 | }; 18 | $provide.value('Alerts', mock); 19 | })); 20 | 21 | beforeEach(inject(function($rootScope) { 22 | scope = $rootScope.$new(); 23 | })); 24 | 25 | it('should show alert messages', inject(function($compile) { 26 | element = angular.element(''); 27 | element = $compile(element)(scope); 28 | scope.$digest(); 29 | 30 | expect(element.find('.alert-item').length).to.eql(2); 31 | })); 32 | }); 33 | -------------------------------------------------------------------------------- /src/components/propertyeditor/propertyeditor.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Directive: propertyEditor', function() { 4 | 5 | // load the directive's module 6 | beforeEach(module('polestar')); 7 | 8 | var element, 9 | scope; 10 | 11 | beforeEach(inject(function($rootScope) { 12 | scope = $rootScope.$new(); 13 | 14 | scope.id = 'foo'; 15 | scope.type = 'boolean'; 16 | scope.name = 'bar'; 17 | scope.enum = undefined; 18 | scope.group = { 19 | bar: 'value' 20 | }; 21 | })); 22 | 23 | it('should make hidden element visible', inject(function($compile) { 24 | element = angular.element(''); 25 | element = $compile(element)(scope); 26 | scope.$digest(); 27 | 28 | expect(element.find('#foo').length).to.eql(1); 29 | })); 30 | }); 31 | -------------------------------------------------------------------------------- /src/components/vlSpecEditor/vlSpecEditor.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Load 4 |
5 | Copy 6 |
7 | 8 |

Vega-lite Shorthand

9 | 10 |
11 | 12 |
13 |
14 | Load 15 |
16 | Copy 17 |
18 |

Vega-lite Encoding

19 |
20 | 21 |
22 |
-------------------------------------------------------------------------------- /src/components/configurationeditor/configurationeditor.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint expr:true */ 4 | 5 | describe('Directive: configurationEditor', function() { 6 | 7 | // load the directive's module 8 | beforeEach(module('polestar')); 9 | 10 | var element, 11 | scope; 12 | 13 | beforeEach(module('polestar', function($provide) { 14 | var mock = {}; 15 | $provide.value('Config', mock); 16 | })); 17 | 18 | beforeEach(inject(function($rootScope, $compile) { 19 | scope = $rootScope.$new(); 20 | 21 | element = angular.element(''); 22 | element = $compile(element)(scope); 23 | scope.$digest(); 24 | })); 25 | 26 | it('should insert form', function() { 27 | expect(element.find('form').length).to.eql(1); 28 | }); 29 | 30 | it('should attach config to scope', function() { 31 | var isolateScope = element.isolateScope(); 32 | expect(isolateScope.Config).to.be.defined; 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/components/schemalist/schemalist.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global vl:true */ 4 | 5 | describe('Directive: schemaList', function() { 6 | 7 | // load the directive's module 8 | beforeEach(module('polestar', function($provide) { 9 | $provide.constant('vl', vl); 10 | })); 11 | 12 | var element, 13 | scope; 14 | 15 | beforeEach(module('polestar', function($provide) { 16 | var mockDataset = { 17 | dataschema: ['foo', 'bar', 'baz'], 18 | stats: { 19 | foo: {}, 20 | bar: {}, 21 | baz: {} 22 | }, 23 | onUpdate: [] 24 | }; 25 | $provide.value('Dataset', mockDataset); 26 | })); 27 | 28 | beforeEach(inject(function($rootScope) { 29 | scope = $rootScope.$new(); 30 | })); 31 | 32 | it('should have field', inject(function($compile) { 33 | element = angular.element(''); 34 | element = $compile(element)(scope); 35 | scope.$digest(); 36 | 37 | expect(element.find('.field-info').length).to.eql(3); 38 | })); 39 | }); 40 | -------------------------------------------------------------------------------- /gulp/e2e-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | 5 | var $ = require('gulp-load-plugins')(); 6 | 7 | var browserSync = require('browser-sync'); 8 | 9 | var paths = gulp.paths; 10 | 11 | // Downloads the selenium webdriver 12 | gulp.task('webdriver-update', $.protractor.webdriver_update); 13 | 14 | gulp.task('webdriver-standalone', $.protractor.webdriver_standalone); 15 | 16 | function runProtractor (done) { 17 | 18 | gulp.src(paths.e2e + '/**/*.js') 19 | .pipe($.protractor.protractor({ 20 | configFile: 'protractor.conf.js', 21 | })) 22 | .on('error', function (err) { 23 | // Make sure failed tests cause gulp to exit non-zero 24 | throw err; 25 | }) 26 | .on('end', function () { 27 | // Close browser sync server 28 | browserSync.exit(); 29 | done(); 30 | }); 31 | } 32 | 33 | gulp.task('protractor', ['protractor:src']); 34 | gulp.task('protractor:src', ['serve:e2e', 'webdriver-update'], runProtractor); 35 | gulp.task('protractor:dist', ['serve:e2e-dist', 'webdriver-update'], runProtractor); 36 | -------------------------------------------------------------------------------- /src/components/shelves/shelves.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .directive('shelves', function() { 5 | 6 | return { 7 | templateUrl: 'components/shelves/shelves.html', 8 | restrict: 'E', 9 | scope: {}, 10 | replace: true, 11 | controller: function($scope, vl, jsondiffpatch, Spec, Config, Dataset, Logger, Pills) { 12 | $scope.Spec = Spec; 13 | $scope.schema = vl.schema.schema; 14 | $scope.pills = Pills; 15 | 16 | $scope.markChange = function() { 17 | Logger.logInteraction(Logger.actions.MARK_CHANGE, Spec.spec.marktype); 18 | }; 19 | 20 | $scope.transpose = function(){ 21 | vl.Encoding.transpose(Spec.spec); 22 | }; 23 | 24 | $scope.clear = function(){ 25 | Spec.reset(); 26 | }; 27 | 28 | $scope.$watch('Spec.spec', function(spec, oldSpec) { 29 | Logger.logInteraction(Logger.actions.SPEC_CHANGE, spec, jsondiffpatch.diff(oldSpec, spec)); 30 | 31 | Spec.update(spec); 32 | }, true /* watch equality rather than reference */); 33 | } 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /src/app/index.scss: -------------------------------------------------------------------------------- 1 | // FIXME should use normalize.scss version instead 2 | // but I got some errors so I don't want to spend too much time here now. 3 | @import "../assets/normalize.scss"; 4 | 5 | // injector 6 | // endinjector 7 | 8 | $fa-font-path: "../../bower_components/fontawesome/fonts"; 9 | @import "../../bower_components/fontawesome/scss/font-awesome.scss"; 10 | 11 | @import "../../bower_components/angular-tooltips/dist/angular-tooltips"; 12 | 13 | $iron: #ccc; 14 | 15 | .type-select { 16 | width: 60px; 17 | } 18 | 19 | .vgspec, .vlspec { 20 | min-height: 100px; 21 | } 22 | 23 | null-filter-directive { 24 | display: block; 25 | margin-bottom: 10px; 26 | } 27 | 28 | /** panes **/ 29 | 30 | .pane.encoding-pane { 31 | width: 230px; 32 | flex-shrink: 0; 33 | } 34 | 35 | 36 | .pane.data-pane { 37 | width: 200px; 38 | flex-shrink: 0; 39 | } 40 | 41 | 42 | .pane.vis-pane{ 43 | flex-grow: 1; 44 | } 45 | 46 | .pane.config-pane { 47 | flex-grow: 0.6; 48 | } 49 | 50 | .pane.vl-pane { 51 | flex-grow: 1; 52 | } 53 | 54 | .pane.vg-pane { 55 | flex-grow: 1.2; 56 | } 57 | 58 | #vis { 59 | @extend .persist-scroll; 60 | overflow: scroll; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polestar", 3 | "version": "0.6.3", 4 | "authors": [ 5 | "Interactive Data Lab (http://idl.cs.washington.edu)" 6 | ], 7 | "dependencies": { 8 | "zeptojs": "~1.1.4", 9 | "angular-cookies": "~1.4.1", 10 | "angular-touch": "~1.4.1", 11 | "angular-sanitize": "~1.4.1", 12 | "angular-ui-router": "~0.2.15", 13 | "angular": "~1.4.1", 14 | "papaparse": "~4.1.1", 15 | "angular-dragdrop": "~1.0.11", 16 | "angular-elastic": "~2.4.2", 17 | "angular-zeroclipboard": "~0.4.0", 18 | "lodash": "~3.10.0", 19 | "d3": "~3.5.6", 20 | "topojson": "~1.6.19", 21 | "fontawesome": "~4.3.0", 22 | "chronicle": "~1.0.9", 23 | "jsondiffpatch": "~0.1.33", 24 | "angular-tooltips": "~0.1.13", 25 | "z-schema": "~3.12.0", 26 | "angular-order-object-by": "~1.1.0", 27 | "angular-websql": "~1.0.2", 28 | "angular-local-storage": "~0.2.2", 29 | "datalib": "^1.3.0", 30 | "vega": "^1.5.0", 31 | "vega-lite-ui": "^0.7.14", 32 | "vega-lite": "^0.7.12" 33 | }, 34 | "devDependencies": { 35 | "angular-mocks": "~1.4.1" 36 | }, 37 | "resolutions": { 38 | "angular": "~1.4.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/components/vgSpecEditor/vgSpecEditor.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global vl:true */ 4 | 5 | describe('Directive: vgSpecEditor', function() { 6 | 7 | // load the directive's module 8 | beforeEach(module('polestar')); 9 | 10 | beforeEach(module('polestar', function($provide) { 11 | $provide.constant('vl', vl); // vl is loaded by karma 12 | 13 | // mock directive (trodrigues's answer in http://stackoverflow.com/questions/17533052) 14 | $provide.factory('uiZeroclipDirective', function() {return { 15 | link: function() {} 16 | };}); 17 | })); 18 | 19 | var element, 20 | scope; 21 | 22 | beforeEach(module('polestar', function($provide) { 23 | var mock = { 24 | vgSpec: {} 25 | }; 26 | $provide.value('Spec', {chart: mock}); 27 | })); 28 | 29 | beforeEach(inject(function($rootScope) { 30 | scope = $rootScope.$new(); 31 | })); 32 | 33 | it('should show source code', inject(function($compile) { 34 | element = angular.element(''); 35 | element = $compile(element)(scope); 36 | scope.$digest(); 37 | 38 | expect(element.find('.vgspec').val()).to.eql('{}'); 39 | })); 40 | }); 41 | -------------------------------------------------------------------------------- /src/components/lyraexport/lyraexport.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .directive('lyraExport', function() { 5 | return { 6 | template: 'Export to lyra', 7 | restrict: 'E', 8 | replace: true, 9 | scope: {}, 10 | controller: function($scope, $timeout, Spec, Alerts) { 11 | $scope.export = function() { 12 | var vgSpec = Spec.vgSpec; 13 | if (!vgSpec) { 14 | Alerts.add('No vega spec present.'); 15 | } 16 | 17 | // Hack needed. See https://github.com/uwdata/lyra/issues/214 18 | vgSpec.marks[0]['lyra.groupType'] = 'layer'; 19 | 20 | var lyraURL = 'http://idl.cs.washington.edu/projects/lyra/app/'; 21 | var lyraWindow = window.open(lyraURL, '_blank'); 22 | 23 | // HACK 24 | // lyraWindow.onload doesn't work across domains 25 | $timeout(function() { 26 | Alerts.add('Please check whether lyra loaded the vega spec correctly. This feature is experimental and may not work.', 5000); 27 | lyraWindow.postMessage({spec: vgSpec}, lyraURL); 28 | }, 5000); 29 | }; 30 | } 31 | }; 32 | }); 33 | -------------------------------------------------------------------------------- /src/components/vlSpecEditor/vlSpecEditor.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global vl:true */ 4 | 5 | describe('Directive: vlSpecEditor', function() { 6 | // load the directive's module 7 | beforeEach(module('polestar')); 8 | 9 | beforeEach(module('polestar', function($provide) { 10 | $provide.constant('vl', vl); // vl is loaded by karma 11 | 12 | // mock directive (trodrigues's answer in http://stackoverflow.com/questions/17533052) 13 | $provide.factory('uiZeroclipDirective', function() {return {};}); 14 | })); 15 | 16 | 17 | 18 | var element, 19 | scope; 20 | 21 | beforeEach(module('polestar', function($provide) { 22 | var mock = { 23 | cleanSpec: {}, 24 | shorthand: 'point.' 25 | }; 26 | $provide.value('Spec', {chart: mock}); 27 | })); 28 | 29 | beforeEach(inject(function($rootScope) { 30 | scope = $rootScope.$new(); 31 | })); 32 | 33 | it('should show source code', inject(function($compile) { 34 | element = angular.element(''); 35 | element = $compile(element)(scope); 36 | scope.$digest(); 37 | 38 | expect(element.find('.vlspec').val()).to.eql('{}'); 39 | expect(element.find('.shorthand').val()).to.eql('point.'); 40 | })); 41 | }); 42 | -------------------------------------------------------------------------------- /src/app/spec/spec.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* global vl */ 3 | /* jshint expr:true */ 4 | 5 | describe('Service: Spec', function() { 6 | 7 | // load the service's module 8 | beforeEach(module('polestar')); 9 | 10 | beforeEach(module('polestar', function($provide) { 11 | $provide.constant('vl', vl); // vl is loaded by karma 12 | })); 13 | 14 | // instantiate service 15 | var Spec; 16 | beforeEach(inject(function(_Spec_) { 17 | Spec = _Spec_; 18 | })); 19 | 20 | it('should be defined', function() { 21 | expect(Spec).to.be.defined; 22 | }); 23 | 24 | it('functions should be defined', function() { 25 | expect(Spec.reset).to.be.defined; 26 | expect(Spec.parseShorthand).to.be.defined; 27 | }); 28 | 29 | describe('_removeEmptyFieldDefs', function() { 30 | describe('empty spec', function() { 31 | it('should be cleaned', function() { 32 | var spec = vl.schema.instantiate(); 33 | Spec._removeEmptyFieldDefs(spec); 34 | expect(vl.keys(spec.encoding).length).to.eql(3); // color, size, shape 35 | }); 36 | }); 37 | }); 38 | 39 | describe('updateSpec', function() { 40 | //TODO write tests 41 | }); 42 | 43 | describe('resetSpec', function() { 44 | //TODO write tests 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/components/schemalistitem/schemalistitem.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global vl:true */ 4 | 5 | describe('Directive: schemaListItem', function () { 6 | 7 | // load the directive's module 8 | beforeEach(module('polestar', function($provide) { 9 | // mock vega 10 | $provide.constant('vg', { 11 | parse: { 12 | spec: function(spec, callback) { 13 | callback(function(opt) { 14 | // jshint unused:false 15 | 16 | return { 17 | width: function() {}, 18 | height: function() {}, 19 | update: function() {}, 20 | renderer: function() {}, 21 | on: function() {} 22 | }; 23 | }); 24 | } 25 | } 26 | }); 27 | $provide.constant('vl', vl); 28 | })); 29 | 30 | 31 | var element, 32 | scope; 33 | 34 | beforeEach(inject(function ($rootScope) { 35 | scope = $rootScope.$new(); 36 | })); 37 | 38 | it('should make hidden element visible', inject(function ($compile) { 39 | element = angular.element(''); 40 | element = $compile(element)(scope); 41 | scope.$digest(); 42 | expect(element.find('span.field-info').length).to.eql(1); 43 | })); 44 | }); -------------------------------------------------------------------------------- /gulp/inject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | 5 | var paths = gulp.paths; 6 | 7 | var $ = require('gulp-load-plugins')(); 8 | 9 | var wiredep = require('wiredep').stream, 10 | series = require('stream-series'); 11 | 12 | gulp.task('inject', ['styles'], function () { 13 | 14 | var injectStyles = gulp.src([ 15 | paths.tmp + '/serve/{app,components}/**/*.css', 16 | '!' + paths.tmp + '/serve/app/vendor.css' 17 | ], { read: false }); 18 | 19 | var injectVendor = gulp.src([ 20 | paths.src + '/vendor/**/*.js' 21 | ], { read: false }); 22 | 23 | var injectScripts = gulp.src([ 24 | paths.src + '/{app,components}/**/*.js', 25 | '!' + paths.src + '/{app,components}/**/*.spec.js', 26 | '!' + paths.src + '/{app,components}/**/*.mock.js' 27 | ]).pipe($.angularFilesort()); 28 | 29 | var injectOptions = { 30 | ignorePath: [paths.src, paths.tmp + '/serve'], 31 | addRootSlash: false 32 | }; 33 | 34 | var wiredepOptions = { 35 | directory: 'bower_components', 36 | exclude: [/bootstrap\.css/, /foundation\.css/], 37 | overrides: { 38 | angular: { 39 | dependencies: { 40 | jquery: '*' 41 | } 42 | } 43 | } 44 | }; 45 | 46 | return gulp.src(paths.src + '/*.html') 47 | .pipe($.inject(injectStyles, injectOptions)) 48 | .pipe($.inject(series(injectVendor, injectScripts), injectOptions)) 49 | .pipe(wiredep(wiredepOptions)) 50 | .pipe(gulp.dest(paths.tmp + '/serve')); 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /src/components/shelves/shelves.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global vl:true */ 4 | /* jshint expr:true */ 5 | 6 | describe('Directive: shelves', function() { 7 | 8 | // load the directive's module 9 | beforeEach(module('polestar')); 10 | 11 | beforeEach(module('polestar', function($provide) { 12 | $provide.constant('vl', vl); // vl is loaded by karma 13 | })); 14 | 15 | var element, 16 | scope, 17 | deferred; 18 | 19 | beforeEach(module('polestar', function($provide) { 20 | // add Directive suffix to mock directives 21 | $provide.value('fieldDefEditorDirective', {}); 22 | $provide.value('functionSelectDirective', {}); 23 | $provide.factory('VegaliteSpecSchema', function($q) { 24 | return { 25 | getSchema: function() { 26 | deferred = $q.defer(); 27 | return deferred.promise; 28 | } 29 | }; 30 | }); 31 | })); 32 | 33 | beforeEach(inject(function($rootScope, $compile) { 34 | scope = $rootScope.$new(); 35 | 36 | element = angular.element(''); 37 | element = $compile(element)(scope); 38 | scope.$digest(); 39 | })); 40 | 41 | it('should insert mark select', function() { 42 | expect(element.find('.markselect').length).to.eql(1); 43 | }); 44 | 45 | it('should attach Spec and schema to scope', function() { 46 | var isolateScope = element.isolateScope(); 47 | expect(isolateScope.Spec).to.be.defined; 48 | expect(isolateScope.schema).to.be.defined; 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /src/app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* globals window */ 3 | 4 | angular.module('polestar', [ 5 | 'ngCookies', 6 | 'ngSanitize', 7 | 'ngTouch', 8 | 'ngDragDrop', 9 | 'monospaced.elastic', 10 | 'zeroclipboard', 11 | 'ui.router', 12 | 'ui.select', 13 | 'Chronicle', 14 | 'LocalStorageModule', 15 | '720kb.tooltips', 16 | 'ngOrderObjectBy', 17 | 'angular-websql', 18 | 'vlui']) 19 | .constant('_', window._) 20 | .constant('vl', window.vl) 21 | .constant('vg', window.vg) 22 | .constant('Papa', window.Papa) 23 | .constant('ZSchema', window.ZSchema) 24 | .constant('Tether', window.Tether) 25 | .constant('Drop', window.Drop) 26 | .constant('Blob', window.Blob) 27 | .constant('URL', window.URL) 28 | .constant('dl', window.dl) 29 | .constant('jsondiffpatch', window.jsondiffpatch) 30 | .config(function(consts) { 31 | window.vl.extend(consts, { 32 | appId: 'polestar' 33 | }); 34 | }) 35 | .config(function(uiZeroclipConfigProvider) { 36 | // config ZeroClipboard 37 | uiZeroclipConfigProvider.setZcConf({ 38 | swfPath: 'bower_components/zeroclipboard/dist/ZeroClipboard.swf' 39 | }); 40 | }) 41 | .config(function(localStorageServiceProvider) { 42 | localStorageServiceProvider.setPrefix('polestar'); 43 | }) 44 | .config(function($stateProvider, $urlRouterProvider) { 45 | $stateProvider 46 | .state('home', { 47 | url: '/', 48 | templateUrl: 'app/main/main.html', 49 | controller: 'MainCtrl' 50 | }); 51 | 52 | $urlRouterProvider.otherwise('/'); 53 | }); 54 | -------------------------------------------------------------------------------- /gulp/styles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | 5 | var paths = gulp.paths; 6 | 7 | var $ = require('gulp-load-plugins')(); 8 | 9 | gulp.task('styles', function () { 10 | 11 | var sassOptions = { 12 | style: 'expanded' 13 | }; 14 | 15 | var injectFiles = gulp.src([ 16 | 'bower_components/vega-lite-ui/vlui.scss', 17 | paths.src + '/{app,components}/**/*.scss', 18 | '!' + paths.src + '/app/index.scss', 19 | '!' + paths.src + '/app/vendor.scss' 20 | ], { read: false }); 21 | 22 | var injectOptions = { 23 | transform: function(filePath) { 24 | filePath = filePath.replace(paths.src + '/app/', ''); 25 | filePath = filePath.replace(paths.src + '/components/', '../components/'); 26 | return '@import \'' + filePath + '\';'; 27 | }, 28 | starttag: '// injector', 29 | endtag: '// endinjector', 30 | addRootSlash: false 31 | }; 32 | 33 | var indexFilter = $.filter('index.scss'); 34 | 35 | return gulp.src([ 36 | paths.src + '/app/index.scss', 37 | paths.src + '/app/vendor.scss' 38 | ]) 39 | .pipe(indexFilter) 40 | .pipe($.inject(injectFiles, injectOptions)) 41 | .pipe(indexFilter.restore()) 42 | // TODO make compass work 43 | // .pipe($.compass({ 44 | // project: paths.src +'/app/', 45 | // css: paths.tmp + '/serve/app/' 46 | // })) 47 | .pipe($.sass(sassOptions)) 48 | 49 | .pipe($.autoprefixer()) 50 | .on('error', function handleError(err) { 51 | console.error(err.toString()); 52 | this.emit('end'); 53 | }) 54 | .pipe(gulp.dest(paths.tmp + '/serve/app/')); 55 | }); 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, University of Washington Interactive Data Lab. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the University of Washington Interactive Data Lab 15 | nor the names of its contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /gulp/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | 5 | var paths = gulp.paths; 6 | 7 | var util = require('util'); 8 | 9 | var browserSync = require('browser-sync'); 10 | 11 | var middleware = require('./proxy'); 12 | 13 | function browserSyncInit(baseDir, files, browser) { 14 | browser = browser === undefined ? 'google chrome' : browser; 15 | 16 | var routes = null; 17 | if(baseDir === paths.src || (util.isArray(baseDir) && baseDir.indexOf(paths.src) !== -1)) { 18 | routes = { 19 | '/bower_components': 'bower_components' 20 | }; 21 | } 22 | 23 | browserSync.instance = browserSync.init(files, { 24 | startPath: '/', 25 | server: { 26 | baseDir: baseDir, 27 | middleware: middleware, 28 | routes: routes 29 | }, 30 | browser: browser 31 | }); 32 | } 33 | 34 | gulp.task('serve', ['watch', 'jshint'], function () { 35 | browserSyncInit([ 36 | paths.tmp + '/serve', 37 | paths.src 38 | ], [ 39 | paths.tmp + '/serve/{app,components}/**/*.css', 40 | paths.src + '/{app,components}/**/*.js', 41 | paths.src + 'src/assets/images/**/*', 42 | paths.src + 'src/assets/data/*', 43 | paths.tmp + '/serve/*.html', 44 | paths.tmp + '/serve/{app,components}/**/*.html', 45 | paths.src + '/{app,components}/**/*.html' 46 | ]); 47 | 48 | gulp.start('test:auto'); 49 | }); 50 | 51 | gulp.task('serve:dist', ['build'], function () { 52 | browserSyncInit(paths.dist); 53 | }); 54 | 55 | gulp.task('serve:e2e', ['inject'], function () { 56 | browserSyncInit([paths.tmp + '/serve', paths.src], null, []); 57 | }); 58 | 59 | gulp.task('serve:e2e-dist', ['build'], function () { 60 | browserSyncInit(paths.dist, null, []); 61 | }); 62 | -------------------------------------------------------------------------------- /src/data/crimea.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "date": "4/1854", "wounds": 0, "other": 110, "disease": 110 }, 3 | { "date": "5/1854", "wounds": 0, "other": 95, "disease": 105 }, 4 | { "date": "6/1854", "wounds": 0, "other": 40, "disease": 95 }, 5 | { "date": "7/1854", "wounds": 0, "other": 140, "disease": 520 }, 6 | { "date": "8/1854", "wounds": 20, "other": 150, "disease": 800 }, 7 | { "date": "9/1854", "wounds": 220, "other": 230, "disease": 740 }, 8 | { "date": "10/1854", "wounds": 305, "other": 310, "disease": 600 }, 9 | { "date": "11/1854", "wounds": 480, "other": 290, "disease": 820 }, 10 | { "date": "12/1854", "wounds": 295, "other": 310, "disease": 1100 }, 11 | { "date": "1/1855", "wounds": 230, "other": 460, "disease": 1440 }, 12 | { "date": "2/1855", "wounds": 180, "other": 520, "disease": 1270 }, 13 | { "date": "3/1855", "wounds": 155, "other": 350, "disease": 935 }, 14 | { "date": "4/1855", "wounds": 195, "other": 195, "disease": 560 }, 15 | { "date": "5/1855", "wounds": 180, "other": 155, "disease": 550 }, 16 | { "date": "6/1855", "wounds": 330, "other": 130, "disease": 650 }, 17 | { "date": "7/1855", "wounds": 260, "other": 130, "disease": 430 }, 18 | { "date": "8/1855", "wounds": 290, "other": 110, "disease": 490 }, 19 | { "date": "9/1855", "wounds": 355, "other": 100, "disease": 290 }, 20 | { "date": "10/1855", "wounds": 135, "other": 95, "disease": 245 }, 21 | { "date": "11/1855", "wounds": 100, "other": 140, "disease": 325 }, 22 | { "date": "12/1855", "wounds": 40, "other": 120, "disease": 215 }, 23 | { "date": "1/1856", "wounds": 0, "other": 160, "disease": 160 }, 24 | { "date": "2/1856", "wounds": 0, "other": 100, "disease": 100 }, 25 | { "date": "3/1856", "wounds": 0, "other": 125, "disease": 90 } 26 | ] -------------------------------------------------------------------------------- /gulp/proxy.js: -------------------------------------------------------------------------------- 1 | /*jshint unused:false */ 2 | 3 | /*************** 4 | 5 | This file allow to configure a proxy system plugged into BrowserSync 6 | in order to redirect backend requests while still serving and watching 7 | files from the web project 8 | 9 | IMPORTANT: The proxy is disabled by default. 10 | 11 | If you want to enable it, watch at the configuration options and finally 12 | change the `module.exports` at the end of the file 13 | 14 | ***************/ 15 | 16 | 'use strict'; 17 | 18 | var httpProxy = require('http-proxy'); 19 | var chalk = require('chalk'); 20 | 21 | /* 22 | * Location of your backend server 23 | */ 24 | var proxyTarget = 'http://server/context/'; 25 | 26 | var proxy = httpProxy.createProxyServer({ 27 | target: proxyTarget 28 | }); 29 | 30 | proxy.on('error', function(error, req, res) { 31 | res.writeHead(500, { 32 | 'Content-Type': 'text/plain' 33 | }); 34 | 35 | console.error(chalk.red('[Proxy]'), error); 36 | }); 37 | 38 | /* 39 | * The proxy middleware is an Express middleware added to BrowserSync to 40 | * handle backend request and proxy them to your backend. 41 | */ 42 | function proxyMiddleware(req, res, next) { 43 | /* 44 | * This test is the switch of each request to determine if the request is 45 | * for a static file to be handled by BrowserSync or a backend request to proxy. 46 | * 47 | * The existing test is a standard check on the files extensions but it may fail 48 | * for your needs. If you can, you could also check on a context in the url which 49 | * may be more reliable but can't be generic. 50 | */ 51 | if (/\.(html|css|js|png|jpg|jpeg|gif|ico|xml|rss|txt|eot|svg|ttf|woff|cur)(\?((r|v|rel|rev)=[\-\.\w]*)?)?$/.test(req.url)) { 52 | next(); 53 | } else { 54 | proxy.web(req, res); 55 | } 56 | } 57 | 58 | /* 59 | * This is where you activate or not your proxy. 60 | * 61 | * The first line activate if and the second one ignored it 62 | */ 63 | 64 | //module.exports = [proxyMiddleware]; 65 | module.exports = []; 66 | -------------------------------------------------------------------------------- /src/components/fielddefeditor/fielddefeditor.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global vl:true */ 4 | 5 | describe('Directive: fieldDefEditor', function() { 6 | 7 | // load the directive's module 8 | beforeEach(module('polestar', function($provide) { 9 | $provide.constant('vl', vl); 10 | $provide.constant('Drop', function() {}); 11 | })); 12 | 13 | var element, scope, $compile; 14 | 15 | beforeEach(module('polestar', function($provide) { 16 | $provide.constant('Dataset', { 17 | stats: { 18 | a: {} 19 | }, 20 | onUpdate: [] 21 | }); 22 | })); 23 | 24 | beforeEach(inject(function($rootScope, _$compile_) { 25 | scope = $rootScope.$new(); 26 | scope.encType = 'x'; 27 | scope.enc = {'x': {}}; 28 | 29 | $compile = _$compile_; 30 | })); 31 | 32 | it('should show title', function() { 33 | element = angular.element(''); 34 | element = $compile(element)(scope); 35 | scope.$digest(); 36 | 37 | expect(element.find('.shelf-label').text().trim()).to.eql('x'); 38 | }); 39 | 40 | describe('fieldDrop', function() { 41 | it('should initially have placeholder', function() { 42 | element = angular.element(''); 43 | element = $compile(element)(scope); 44 | scope.$digest(); 45 | expect(element.find('.placeholder').length).to.eql(1); 46 | }); 47 | 48 | it('should show correct field name when dropped', function() { 49 | // jshint unused:false 50 | //TODO 51 | }); 52 | }); 53 | 54 | describe('shelfProperties', function() { 55 | it('should change properties correctly', function() { 56 | // jshint unused:false 57 | //TODO 58 | }); 59 | }); 60 | 61 | describe('shelfFunctions', function() { 62 | it('should change function correctly', function() { 63 | // jshint unused:false 64 | //TODO 65 | }); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-gulp-angular": { 3 | "props": { 4 | "paths": { 5 | "src": "src", 6 | "dist": "dist", 7 | "e2e": "e2e", 8 | "tmp": ".tmp" 9 | }, 10 | "angularVersion": "~1.3.4", 11 | "angularModules": [ 12 | { 13 | "name": "angular-animate", 14 | "module": "ngAnimate" 15 | }, 16 | { 17 | "name": "angular-cookies", 18 | "module": "ngCookies" 19 | }, 20 | { 21 | "name": "angular-touch", 22 | "module": "ngTouch" 23 | }, 24 | { 25 | "name": "angular-sanitize", 26 | "module": "ngSanitize" 27 | } 28 | ], 29 | "jQuery": { 30 | "name": "zeptojs", 31 | "version": "~1.1.4" 32 | }, 33 | "resource": { 34 | "name": null, 35 | "version": null, 36 | "module": null 37 | }, 38 | "router": { 39 | "name": "angular-ui-router", 40 | "version": "~0.2.13", 41 | "module": "ui.router" 42 | }, 43 | "ui": { 44 | "key": "none", 45 | "name": null, 46 | "version": null, 47 | "module": null 48 | }, 49 | "cssPreprocessor": { 50 | "key": "node-sass", 51 | "extension": "scss", 52 | "module": "gulp-sass", 53 | "version": "~1.1.0" 54 | }, 55 | "jsPreprocessor": { 56 | "key": "none", 57 | "extension": "js", 58 | "srcExtension": "js", 59 | "module": null, 60 | "version": null 61 | }, 62 | "htmlPreprocessor": { 63 | "key": "none", 64 | "extension": "html", 65 | "module": null, 66 | "version": null 67 | }, 68 | "bootstrapComponents": { 69 | "name": null, 70 | "version": null, 71 | "key": null, 72 | "module": null 73 | }, 74 | "foundationComponents": { 75 | "name": null, 76 | "version": null, 77 | "key": null, 78 | "module": null 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Pole✭ 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 57 | 58 | -------------------------------------------------------------------------------- /src/app/main/main.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .controller('MainCtrl', function($scope, $document, Spec, Dataset, Config, consts, Chronicle, Logger, Bookmarks) { 5 | $scope.Spec = Spec; 6 | $scope.Dataset = Dataset; 7 | $scope.Config = Config; 8 | $scope.Logger = Logger; 9 | $scope.Bookmarks = Bookmarks; 10 | $scope.consts = consts; 11 | $scope.showDevPanel = false; 12 | 13 | // undo/redo support 14 | 15 | $scope.canUndo = false; 16 | $scope.canRedo = false; 17 | 18 | // bookmark 19 | $scope.showBookmark = false; 20 | $scope.hideBookmark = function() { 21 | $scope.showBookmark = false; 22 | }; 23 | 24 | // load bookmarks from local storage 25 | Bookmarks.load(); 26 | 27 | // initialize undo after we have a dataset 28 | Dataset.update(Dataset.dataset).then(function() { 29 | Config.updateDataset(Dataset.dataset); 30 | 31 | $scope.chron = Chronicle.record('Spec.spec', $scope, true, 32 | ['Dataset.dataset', 'Dataset.dataschema','Dataset.stats', 'Config.config']); 33 | 34 | $scope.canUndoRedo = function() { 35 | $scope.canUndo = $scope.chron.canUndo(); 36 | $scope.canRedo = $scope.chron.canRedo(); 37 | }; 38 | $scope.chron.addOnAdjustFunction($scope.canUndoRedo); 39 | $scope.chron.addOnUndoFunction($scope.canUndoRedo); 40 | $scope.chron.addOnRedoFunction($scope.canUndoRedo); 41 | 42 | $scope.chron.addOnUndoFunction(function() { 43 | Logger.logInteraction(Logger.actions.UNDO); 44 | }); 45 | $scope.chron.addOnRedoFunction(function() { 46 | Logger.logInteraction(Logger.actions.REDO); 47 | }); 48 | 49 | angular.element($document).on('keydown', function(e) { 50 | if (e.keyCode === 'Z'.charCodeAt(0) && (e.ctrlKey || e.metaKey) && !e.shiftKey) { 51 | $scope.chron.undo(); 52 | $scope.$digest(); 53 | return false; 54 | } else if (e.keyCode === 'Y'.charCodeAt(0) && (e.ctrlKey || e.metaKey)) { 55 | $scope.chron.redo(); 56 | $scope.$digest(); 57 | return false; 58 | } else if (e.keyCode === 'Z'.charCodeAt(0) && (e.ctrlKey || e.metaKey) && e.shiftKey) { 59 | $scope.chron.redo(); 60 | $scope.$digest(); 61 | return false; 62 | } 63 | }); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polestar", 3 | "version": "0.6.3", 4 | "description": "Visualization specification interface.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/uwdata/polestar.git" 8 | }, 9 | "author": { 10 | "name": "UW Interactive Data Lab", 11 | "url": "http://idl.cs.washington.edu" 12 | }, 13 | "collaborators": [ 14 | "Dominik Moritz (http://domoritz.de)", 15 | "Kanit Wongsuphasawat (http://kanitw.yellowpigz.com)", 16 | "Jeffrey Heer (http://jheer.org)" 17 | ], 18 | "license": "BSD-3-Clause", 19 | "scripts": { 20 | "test": "gulp test" 21 | }, 22 | "devDependencies": { 23 | "browser-sync": "~2.7.13", 24 | "chalk": "~1.0.0", 25 | "del": "~1.2.0", 26 | "gulp": "~3.9.0", 27 | "gulp-angular-filesort": "^1.1.1", 28 | "gulp-angular-templatecache": "^1.7.0", 29 | "gulp-autoprefixer": "~2.3.1", 30 | "gulp-bump": "^0.3.1", 31 | "gulp-compass": "^2.1.0", 32 | "gulp-consolidate": "~0.1.2", 33 | "gulp-csso": "~1.0.0", 34 | "gulp-filter": "~2.0.2", 35 | "gulp-flatten": "~0.1.0", 36 | "gulp-inject": "~1.3.1", 37 | "gulp-jshint": "~1.11.1", 38 | "gulp-karma": "~0.0.4", 39 | "gulp-load-plugins": "~1.0.0-rc", 40 | "gulp-minify-html": "~1.0.3", 41 | "gulp-ng-annotate": "^1.0.0", 42 | "gulp-protractor": "~1.0.0", 43 | "gulp-rename": "~1.2.2", 44 | "gulp-replace": "~0.5.3", 45 | "gulp-rev": "~5.0.1", 46 | "gulp-rev-replace": "~0.4.2", 47 | "gulp-sass": "~2.0.3", 48 | "gulp-size": "~1.2.2", 49 | "gulp-uglify": "~1.2.0", 50 | "gulp-useref": "~1.2.0", 51 | "http-proxy": "~1.11.1", 52 | "jshint-stylish": "~2.0.1", 53 | "karma-chai": "^0.1.0", 54 | "karma-chai-jquery": "^1.0.0", 55 | "karma-chrome-launcher": "^0.2.0", 56 | "karma-jquery": "^0.1.0", 57 | "karma-mocha": "^0.2.0", 58 | "karma-mocha-reporter": "^1.0.2", 59 | "karma-phantomjs-launcher": "~0.2.0", 60 | "karma-sinon-chai": "^1.0.0", 61 | "main-bower-files": "~2.8.2", 62 | "phantomjs-polyfill": "0.0.1", 63 | "protractor": "~2.1.0", 64 | "request": "^2.58.0", 65 | "require-dir": "~0.3.0", 66 | "stream-series": "^0.1.1", 67 | "uglify-save-license": "~0.4.1", 68 | "wiredep": "^3.0.0-beta", 69 | "yargs": "^3.14.0" 70 | }, 71 | "engines": { 72 | "node": ">=0.10.0" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/components/fielddefeditor/fielddefeditor.scss: -------------------------------------------------------------------------------- 1 | /* FIXME should be in vl-ui*/ 2 | $pill-radius: 3px; 3 | $pill-height: 22px; 4 | $pill-min-width: 150px; 5 | $text-color: #212121; 6 | 7 | .shelf-group{ 8 | border: 1px solid #ddd; 9 | border-radius: $pill-radius; 10 | margin-bottom: 5px; 11 | font-size: 11px; 12 | } 13 | 14 | .shelf{ 15 | border-radius: $pill-radius; 16 | background-color: #ddd; 17 | width: 100%; 18 | display: flex; 19 | height: 22px; 20 | 21 | &.disabled { 22 | opacity: 0.5; 23 | } 24 | 25 | .shelf-label { 26 | @extend .noselect; 27 | display: block; 28 | width: 40px; 29 | margin: 0 0.2rem; 30 | // text-align: right; 31 | line-height: $pill-height; 32 | cursor: pointer; 33 | flex-shrink: 0; 34 | 35 | .fa { 36 | margin-top: 4px; 37 | opacity: 0.5; 38 | } 39 | &.expanded .fa, &:hover .fa { 40 | opacity: 1; 41 | } 42 | } 43 | 44 | .field-drop { 45 | display: flex; 46 | flex-grow: 1; 47 | width: auto; 48 | box-sizing: border-box; 49 | border: 1px solid transparent; 50 | border-radius: 3px; 51 | height: $pill-height; 52 | line-height: $pill-height; 53 | 54 | .field-info { 55 | margin-right: 0; 56 | } 57 | 58 | .placeholder { 59 | display: block; 60 | width: 100%; 61 | padding: 0 0.5em; 62 | border: 1px solid #ccc; 63 | background-color: white; 64 | line-height: $pill-height - 2; 65 | border-radius: $pill-radius; 66 | text-overflow: ellipsis; 67 | font-weight: normal; 68 | color: #aaa; 69 | } 70 | } 71 | 72 | .field-drop.drop-active { 73 | border: 1px dashed #4CAF50; 74 | 75 | background-color: #C8E6C9; 76 | .placeholder { 77 | background-color: #C8E6C9; 78 | color: $text-color; 79 | } 80 | .field-info { 81 | opacity: 0.5; 82 | } 83 | } 84 | } 85 | 86 | .shelf-properties { 87 | min-width: 250px; 88 | } 89 | 90 | .shelf-functions { 91 | width: 160px; 92 | } 93 | 94 | .shelf-properties, .shelf-functions{ 95 | label { 96 | display: inline-block; 97 | 98 | &.prop-label { 99 | width: 60px; 100 | text-align: right; 101 | margin-right: 3px; 102 | } 103 | 104 | &.func-label, &.type-label { 105 | min-width: 75px; 106 | margin-right: 5px; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/components/functionselect/functionselect.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global vl:true */ 4 | 5 | describe('Directive: functionSelect', function() { 6 | 7 | // load the directive's module 8 | beforeEach(module('polestar', function($provide) { 9 | $provide.constant('vl', vl); 10 | })); 11 | 12 | var element, scope, $compile; 13 | 14 | beforeEach(inject(function($rootScope, _$compile_) { 15 | scope = $rootScope.$new(); 16 | scope.schema = { 17 | properties: { 18 | aggregate: { 19 | supportedEnums: { 20 | Q: ['a', 'b'], 21 | undefined: [] 22 | } 23 | }, 24 | timeUnit: { 25 | supportedEnums: { 26 | T: ['f1','f2'] 27 | } 28 | }, 29 | bin: { 30 | supportedTypes: { 31 | Q: true 32 | } 33 | } 34 | } 35 | }; 36 | scope.pills = { 37 | x: { type: 'Q', name: 'x'}, 38 | y: { aggregate: 'count', name:'*'}, 39 | color: { type: 'T', name: 'c'}, 40 | update: function() {} 41 | }; 42 | scope.enc = { 43 | x: { type: 'Q', name: 'x'}, 44 | y: { aggregate: 'count', name:'*'}, 45 | color: { type: 'T', name: 'c'}, 46 | }; 47 | scope.encType = 'x'; 48 | scope.encType2 = 'y'; 49 | scope.encType3 = 'color'; 50 | 51 | $compile = _$compile_; 52 | })); 53 | 54 | it('should have correct number of radio', function() { 55 | element = angular.element(''); 56 | element = $compile(element)(scope); 57 | scope.$digest(); 58 | expect(element.find('input').length).to.eql(4); 59 | }); 60 | 61 | it('should have correct number of radio', function() { 62 | element = angular.element(''); 63 | element = $compile(element)(scope); 64 | scope.$digest(); 65 | expect(element.find('input').length).to.eql(3); 66 | }); 67 | 68 | it('should not show other options for count field', function() { 69 | element = angular.element(''); 70 | element = $compile(element)(scope); 71 | scope.$digest(); 72 | scope.pills.y = { aggregate:'count', name: '*'}; 73 | scope.$digest(); 74 | expect(element.find('input').length).to.eql(1); 75 | }); 76 | 77 | }); 78 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var wiredep = require('wiredep'); 4 | 5 | var bowerDeps = wiredep({ 6 | directory: 'bower_components', 7 | dependencies: true, 8 | devDependencies: true 9 | }); 10 | 11 | var src = 'src', 12 | tmp = '.tmp'; 13 | 14 | var testFiles = [ 15 | // add bind polyfill since Function.prototype.bind is missing from PhantomJS 16 | './node_modules/phantomjs-polyfill/bind-polyfill.js', 17 | ].concat(bowerDeps.js).concat([ 18 | src + '/app/index.js', 19 | src + '/**/*.js', 20 | src + '/vendor/*.js', 21 | tmp + '/partials/templateCacheHtml.js' 22 | ]); 23 | 24 | // console.log('testFiles', testFiles); 25 | 26 | module.exports = function(config) { 27 | config.set({ 28 | 29 | // base path that will be used to resolve all patterns (eg. files, exclude) 30 | basePath: '', 31 | 32 | // frameworks to use 33 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 34 | frameworks: ['mocha', 'chai-jquery', 'jquery-1.8.3', 'sinon-chai'], 35 | 36 | plugins: [ 37 | 'karma-mocha', 38 | 'karma-chai', 39 | 'karma-sinon-chai', 40 | 'karma-chrome-launcher', 41 | 'karma-phantomjs-launcher', 42 | 'karma-jquery', 43 | 'karma-chai-jquery', 44 | 'karma-mocha-reporter' 45 | ], 46 | 47 | // list of files / patterns to load in the browser 48 | files: testFiles, 49 | 50 | // list of files to exclude 51 | exclude: [], 52 | 53 | // preprocess matching files before serving them to the browser 54 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 55 | preprocessors: {}, 56 | 57 | // test results reporter to use 58 | // possible values: 'dots', 'progress' 59 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 60 | reporters: ['mocha'], 61 | 62 | // web server port 63 | port: 9875, 64 | 65 | // enable / disable colors in the output (reporters and logs) 66 | colors: true, 67 | 68 | // level of logging 69 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 70 | logLevel: config.LOG_INFO, 71 | 72 | 73 | // enable / disable watching file and executing tests whenever any file changes 74 | autoWatch: true, 75 | 76 | // start these browsers 77 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 78 | browsers: ['PhantomJS'], 79 | 80 | // Continuous Integration mode 81 | // if true, Karma captures browsers, runs the tests and exits 82 | singleRun: false, 83 | 84 | client: { 85 | mocha: { 86 | reporter: 'html', // change Karma's debug.html to the mocha web reporter 87 | ui: 'bdd' 88 | } 89 | } 90 | }); 91 | }; 92 | -------------------------------------------------------------------------------- /src/components/shelves/shelves.html: -------------------------------------------------------------------------------- 1 |
2 | Clear 3 |

Encoding

4 | 5 |
6 |

Positional

7 | 11 | 12 | 17 | 18 | 23 | 24 | 28 | 29 |
30 |
31 |
32 | 35 |
36 |

Marks

37 | 38 | 42 | 43 | 47 | 48 | 52 | 53 | 57 | 58 | 62 | 63 |
64 | 65 | 69 |
-------------------------------------------------------------------------------- /gulp/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | 5 | var paths = gulp.paths; 6 | 7 | var $ = require('gulp-load-plugins')({ 8 | pattern: ['gulp-*', 'main-bower-files', 'uglify-save-license', 'del'] 9 | }); 10 | 11 | gulp.task('partials', function () { 12 | return gulp.src([ 13 | paths.src + '/{app,components}/**/*.html', 14 | paths.tmp + '/{app,components}/**/*.html' 15 | ]) 16 | .pipe($.minifyHtml({ 17 | empty: true, 18 | spare: true, 19 | quotes: true 20 | })) 21 | .pipe($.angularTemplatecache('templateCacheHtml.js', { 22 | module: 'polestar' 23 | })) 24 | .pipe(gulp.dest(paths.tmp + '/partials/')); 25 | }); 26 | 27 | gulp.task('html', ['inject', 'partials'], function () { 28 | var partialsInjectFile = gulp.src(paths.tmp + '/partials/templateCacheHtml.js', { read: false }); 29 | var partialsInjectOptions = { 30 | starttag: '', 31 | ignorePath: paths.tmp + '/partials', 32 | addRootSlash: false 33 | }; 34 | 35 | var htmlFilter = $.filter('*.html'); 36 | var jsFilter = $.filter('**/*.js'); 37 | var cssFilter = $.filter('**/*.css'); 38 | var assets; 39 | 40 | return gulp.src(paths.tmp + '/serve/*.html') 41 | .pipe($.inject(partialsInjectFile, partialsInjectOptions)) 42 | .pipe(assets = $.useref.assets()) 43 | .pipe($.rev()) 44 | .pipe(jsFilter) 45 | .pipe($.ngAnnotate()) 46 | .pipe($.uglify({preserveComments: $.uglifySaveLicense})) 47 | .pipe(jsFilter.restore()) 48 | .pipe(cssFilter) 49 | .pipe($.replace('../../bower_components/fontawesome/fonts', '../fonts')) 50 | .pipe($.csso()) 51 | .pipe(cssFilter.restore()) 52 | .pipe(assets.restore()) 53 | .pipe($.useref()) 54 | .pipe($.revReplace()) 55 | .pipe(htmlFilter) 56 | .pipe($.minifyHtml({ 57 | empty: true, 58 | spare: true, 59 | quotes: true 60 | })) 61 | .pipe(htmlFilter.restore()) 62 | .pipe(gulp.dest(paths.dist + '/')) 63 | .pipe($.size({ title: paths.dist + '/', showFiles: true })); 64 | }); 65 | 66 | gulp.task('assets', function () { 67 | return gulp.src(paths.src + '/assets/**/*') 68 | .pipe(gulp.dest(paths.dist + '/assets/')); 69 | }); 70 | 71 | gulp.task('data', function () { 72 | return gulp.src(paths.src + '/data/*') 73 | .pipe(gulp.dest(paths.dist + '/data/')); 74 | }); 75 | 76 | gulp.task('fonts', function () { 77 | return gulp.src($.mainBowerFiles()) 78 | .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}')) 79 | .pipe($.flatten()) 80 | .pipe(gulp.dest(paths.dist + '/fonts/')); 81 | }); 82 | 83 | gulp.task('misc', function () { 84 | return gulp.src(paths.src + '/**/*.ico') 85 | .pipe(gulp.dest(paths.dist + '/')); 86 | }); 87 | 88 | gulp.task('zeroclipboard', function () { 89 | return gulp.src('bower_components/zeroclipboard/dist/ZeroClipboard.swf') 90 | .pipe(gulp.dest(paths.dist + '/bower_components/zeroclipboard/dist/')); 91 | }); 92 | 93 | gulp.task('clean', function (done) { 94 | $.del([paths.dist + '/', paths.tmp + '/'], done); 95 | }); 96 | 97 | gulp.task('build', ['html', 'assets', 'data', 'fonts', 'misc', 'zeroclipboard']); 98 | -------------------------------------------------------------------------------- /src/app/main/main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 26 |

Pole✭

27 |
28 | 29 |
30 |
31 |
32 |
33 |
34 | 35 |
36 |

Data

37 | 38 |
39 |
40 |
41 | 42 |
43 | 44 |
45 | 60 |
61 |
62 |
63 |
64 |
65 |
66 | 67 |
68 |
69 |
70 |
71 | 72 |
73 |
74 |
75 | 76 |
77 |
78 | 79 |
80 |
81 |
82 | 85 | -------------------------------------------------------------------------------- /src/components/fielddefeditor/fielddefeditor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .directive('fieldDefEditor', function(Dataset, Pills, _, Drop, Logger) { 5 | return { 6 | templateUrl: 'components/fielddefeditor/fielddefeditor.html', 7 | restrict: 'E', 8 | replace: true, 9 | scope: { 10 | encType: '=', 11 | enc: '=', 12 | 13 | schema: '=fieldDefSchema', 14 | marktype: '=' 15 | }, 16 | link: function(scope, element /*, attrs*/) { 17 | var propsPopup, funcsPopup; 18 | 19 | scope.allowedCasting = { 20 | Q: ['Q', 'O', 'N'], 21 | O: ['O', 'N'], 22 | N: ['N', 'O'], 23 | T: ['T', 'O', 'N'], 24 | G: ['G', 'O', 'N'] 25 | }; 26 | 27 | scope.Dataset = Dataset; 28 | scope.typeNames = Dataset.typeNames; 29 | scope.pills = Pills.pills; 30 | 31 | function fieldPill(){ 32 | return Pills.pills[scope.encType]; 33 | } 34 | 35 | propsPopup = new Drop({ 36 | content: element.find('.shelf-properties')[0], 37 | target: element.find('.shelf-label')[0], 38 | position: 'bottom left', 39 | openOn: 'click' 40 | }); 41 | 42 | scope.fieldInfoPopupContent = element.find('.shelf-functions')[0]; 43 | 44 | scope.removeField = function() { 45 | Pills.remove(scope.encType); 46 | }; 47 | 48 | scope.fieldDragStart = function() { 49 | Pills.dragStart(Pills[scope.encType], scope.encType); 50 | }; 51 | 52 | scope.fieldDragStop = function() { 53 | Pills.dragStop(); 54 | }; 55 | 56 | scope.fieldDropped = function() { 57 | var pill = fieldPill(); 58 | if (funcsPopup) { 59 | funcsPopup = null; 60 | } 61 | 62 | // validate type 63 | var types = scope.schema.properties.type.enum; 64 | if (!_.contains(types, pill.type)) { 65 | // if existing type is not supported 66 | pill.type = types[0]; 67 | } 68 | 69 | // TODO validate timeUnit / aggregate 70 | 71 | Pills.dragDrop(scope.encType); 72 | Logger.logInteraction(Logger.actions.FIELD_DROP, scope.enc[scope.encType]); 73 | }; 74 | 75 | // when each of the fieldPill property in fieldDef changes, update the pill 76 | // ['name', 'type', 'aggregate', 'bin', 'timeUnit'].forEach( function(prop) { 77 | // scope.$watch('enc[encType].'+prop, function(val){ 78 | // var pill = fieldPill(); 79 | // if(pill && val !== pill[prop]){ 80 | // pill[prop] = val; 81 | // } 82 | // }, true); 83 | // }); 84 | 85 | scope.$watch('enc[encType]', function(field) { 86 | Pills.pills[scope.encType] = field ? _.cloneDeep(field) : {}; 87 | }, true); 88 | 89 | scope.$watchGroup(['allowedCasting[Dataset.dataschema.byName[enc[encType].name].type]', 'enc[encType].aggregate'], function(arr){ 90 | var allowedTypes = arr[0], aggregate=arr[1]; 91 | scope.allowedTypes = aggregate === 'count' ? ['Q'] : allowedTypes; 92 | }); 93 | 94 | 95 | } 96 | 97 | }; 98 | }); 99 | -------------------------------------------------------------------------------- /src/data/burtin.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Bacteria":"Aerobacter aerogenes", 4 | "Penicilin":870, 5 | "Streptomycin":1, 6 | "Neomycin":1.6, 7 | "Gram_Staining":"negative", 8 | "Genus": "other" 9 | }, 10 | { 11 | "Bacteria":"Brucella abortus", 12 | "Penicilin":1, 13 | "Streptomycin":2, 14 | "Neomycin":0.02, 15 | "Gram_Staining":"negative", 16 | "Genus": "other" 17 | }, 18 | { 19 | "Bacteria":"Brucella anthracis", 20 | "Penicilin":0.001, 21 | "Streptomycin":0.01, 22 | "Neomycin":0.007, 23 | "Gram_Staining":"positive", 24 | "Genus": "other" 25 | }, 26 | { 27 | "Bacteria":"Diplococcus pneumoniae", 28 | "Penicilin":0.005, 29 | "Streptomycin":11, 30 | "Neomycin":10, 31 | "Gram_Staining":"positive", 32 | "Genus": "other" 33 | }, 34 | { 35 | "Bacteria":"Escherichia coli", 36 | "Penicilin":100, 37 | "Streptomycin":0.4, 38 | "Neomycin":0.1, 39 | "Gram_Staining":"negative", 40 | "Genus": "other" 41 | }, 42 | { 43 | "Bacteria":"Klebsiella pneumoniae", 44 | "Penicilin":850, 45 | "Streptomycin":1.2, 46 | "Neomycin":1, 47 | "Gram_Staining":"negative", 48 | "Genus": "other" 49 | }, 50 | { 51 | "Bacteria":"Mycobacterium tuberculosis", 52 | "Penicilin":800, 53 | "Streptomycin":5, 54 | "Neomycin":2, 55 | "Gram_Staining":"negative", 56 | "Genus": "other" 57 | }, 58 | { 59 | "Bacteria":"Proteus vulgaris", 60 | "Penicilin":3, 61 | "Streptomycin":0.1, 62 | "Neomycin":0.1, 63 | "Gram_Staining":"negative", 64 | "Genus": "other" 65 | }, 66 | { 67 | "Bacteria":"Pseudomonas aeruginosa", 68 | "Penicilin":850, 69 | "Streptomycin":2, 70 | "Neomycin":0.4, 71 | "Gram_Staining":"negative", 72 | "Genus": "other" 73 | }, 74 | { 75 | "Bacteria":"Salmonella (Eberthella) typhosa", 76 | "Penicilin":1, 77 | "Streptomycin":0.4, 78 | "Neomycin":0.008, 79 | "Gram_Staining":"negative", 80 | "Genus": "Salmonella" 81 | }, 82 | { 83 | "Bacteria":"Salmonella schottmuelleri", 84 | "Penicilin":10, 85 | "Streptomycin":0.8, 86 | "Neomycin":0.09, 87 | "Gram_Staining":"negative", 88 | "Genus": "Salmonella" 89 | }, 90 | { 91 | "Bacteria":"Staphylococcus albus", 92 | "Penicilin":0.007, 93 | "Streptomycin":0.1, 94 | "Neomycin":0.001, 95 | "Gram_Staining":"positive", 96 | "Genus": "Staphylococcus" 97 | }, 98 | { 99 | "Bacteria":"Staphylococcus aureus", 100 | "Penicilin":0.03, 101 | "Streptomycin":0.03, 102 | "Neomycin":0.001, 103 | "Gram_Staining":"positive", 104 | "Genus": "Staphylococcus" 105 | }, 106 | { 107 | "Bacteria":"Streptococcus fecalis", 108 | "Penicilin":1, 109 | "Streptomycin":1, 110 | "Neomycin":0.1, 111 | "Gram_Staining":"positive", 112 | "Genus": "Streptococcus" 113 | }, 114 | { 115 | "Bacteria":"Streptococcus hemolyticus", 116 | "Penicilin":0.001, 117 | "Streptomycin":14, 118 | "Neomycin":10, 119 | "Gram_Staining":"positive", 120 | "Genus": "Streptococcus" 121 | }, 122 | { 123 | "Bacteria":"Streptococcus viridans", 124 | "Penicilin":0.005, 125 | "Streptomycin":10, 126 | "Neomycin":40, 127 | "Gram_Staining":"positive", 128 | "Genus": "Streptococcus" 129 | } 130 | ] -------------------------------------------------------------------------------- /src/data/driving.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"side": "left", "year": 1956, "miles": 3675, "gas": 2.38}, 3 | {"side": "right", "year": 1957, "miles": 3706, "gas": 2.40}, 4 | {"side": "bottom", "year": 1958, "miles": 3766, "gas": 2.26}, 5 | {"side": "top", "year": 1959, "miles": 3905, "gas": 2.31}, 6 | {"side": "right", "year": 1960, "miles": 3935, "gas": 2.27}, 7 | {"side": "bottom", "year": 1961, "miles": 3977, "gas": 2.25}, 8 | {"side": "right", "year": 1962, "miles": 4085, "gas": 2.22}, 9 | {"side": "bottom", "year": 1963, "miles": 4218, "gas": 2.12}, 10 | {"side": "bottom", "year": 1964, "miles": 4369, "gas": 2.11}, 11 | {"side": "bottom", "year": 1965, "miles": 4538, "gas": 2.14}, 12 | {"side": "top", "year": 1966, "miles": 4676, "gas": 2.14}, 13 | {"side": "bottom", "year": 1967, "miles": 4827, "gas": 2.14}, 14 | {"side": "right", "year": 1968, "miles": 5038, "gas": 2.13}, 15 | {"side": "right", "year": 1969, "miles": 5207, "gas": 2.07}, 16 | {"side": "right", "year": 1970, "miles": 5376, "gas": 2.01}, 17 | {"side": "bottom", "year": 1971, "miles": 5617, "gas": 1.93}, 18 | {"side": "bottom", "year": 1972, "miles": 5973, "gas": 1.87}, 19 | {"side": "right", "year": 1973, "miles": 6154, "gas": 1.90}, 20 | {"side": "left", "year": 1974, "miles": 5943, "gas": 2.34}, 21 | {"side": "bottom", "year": 1975, "miles": 6111, "gas": 2.31}, 22 | {"side": "bottom", "year": 1976, "miles": 6389, "gas": 2.32}, 23 | {"side": "top", "year": 1977, "miles": 6630, "gas": 2.36}, 24 | {"side": "bottom", "year": 1978, "miles": 6883, "gas": 2.23}, 25 | {"side": "left", "year": 1979, "miles": 6744, "gas": 2.68}, 26 | {"side": "left", "year": 1980, "miles": 6672, "gas": 3.30}, 27 | {"side": "right", "year": 1981, "miles": 6732, "gas": 3.30}, 28 | {"side": "right", "year": 1982, "miles": 6835, "gas": 2.92}, 29 | {"side": "right", "year": 1983, "miles": 6943, "gas": 2.66}, 30 | {"side": "right", "year": 1984, "miles": 7130, "gas": 2.48}, 31 | {"side": "right", "year": 1985, "miles": 7323, "gas": 2.36}, 32 | {"side": "left", "year": 1986, "miles": 7558, "gas": 1.76}, 33 | {"side": "top", "year": 1987, "miles": 7770, "gas": 1.76}, 34 | {"side": "bottom", "year": 1988, "miles": 8089, "gas": 1.68}, 35 | {"side": "left", "year": 1989, "miles": 8397, "gas": 1.75}, 36 | {"side": "top", "year": 1990, "miles": 8529, "gas": 1.88}, 37 | {"side": "right", "year": 1991, "miles": 8535, "gas": 1.78}, 38 | {"side": "right", "year": 1992, "miles": 8662, "gas": 1.69}, 39 | {"side": "left", "year": 1993, "miles": 8855, "gas": 1.60}, 40 | {"side": "bottom", "year": 1994, "miles": 8909, "gas": 1.59}, 41 | {"side": "bottom", "year": 1995, "miles": 9150, "gas": 1.60}, 42 | {"side": "top", "year": 1996, "miles": 9192, "gas": 1.67}, 43 | {"side": "right", "year": 1997, "miles": 9416, "gas": 1.65}, 44 | {"side": "bottom", "year": 1998, "miles": 9590, "gas": 1.39}, 45 | {"side": "right", "year": 1999, "miles": 9687, "gas": 1.50}, 46 | {"side": "top", "year": 2000, "miles": 9717, "gas": 1.89}, 47 | {"side": "left", "year": 2001, "miles": 9699, "gas": 1.77}, 48 | {"side": "bottom", "year": 2002, "miles": 9814, "gas": 1.64}, 49 | {"side": "right", "year": 2003, "miles": 9868, "gas": 1.86}, 50 | {"side": "left", "year": 2004, "miles": 9994, "gas": 2.14}, 51 | {"side": "left", "year": 2005, "miles": 10067, "gas": 2.53}, 52 | {"side": "right", "year": 2006, "miles": 10037, "gas": 2.79}, 53 | {"side": "right", "year": 2007, "miles": 10025, "gas": 2.95}, 54 | {"side": "left", "year": 2008, "miles": 9880, "gas": 3.31}, 55 | {"side": "bottom", "year": 2009, "miles": 9657, "gas": 2.38}, 56 | {"side": "left", "year": 2010, "miles": 9596, "gas": 2.61} 57 | ] -------------------------------------------------------------------------------- /src/components/fielddefeditor/fielddefeditor.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
5 | 6 | {{ encType }} 7 |
8 | 9 |
14 | 15 | 31 | 32 | 33 | 34 | drop a field here 35 | 36 |
37 |
38 |
39 | 57 | 78 |
79 |
80 | -------------------------------------------------------------------------------- /src/app/spec/spec.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc service 5 | * @name polestar.Spec 6 | * @description 7 | * # Spec 8 | * Service in the polestar. 9 | */ 10 | angular.module('polestar') 11 | .service('Spec', function(_, vl, ZSchema, Alerts, Config, Dataset) { 12 | var Spec = { 13 | /** @type {Object} verbose spec edited by the UI */ 14 | spec: null, 15 | chart:{ 16 | /** @type {Object} concise spec generated */ 17 | vlSpec: null, 18 | /** @type {Encoding} encoding object from the spec */ 19 | encoding: null, 20 | /** @type {String} generated vl shorthand */ 21 | shorthand: null, 22 | /** @type {Object} generated vega spec */ 23 | vgSpec: null 24 | } 25 | }; 26 | 27 | Spec._removeEmptyFieldDefs = function(spec) { 28 | spec.encoding = _.omit(spec.encoding, function(fieldDef, encType) { 29 | return !fieldDef || (fieldDef.name === undefined && fieldDef.value === undefined) || 30 | (spec.marktype && ! vl.schema.schema.properties.encoding.properties[encType].supportedMarktypes[spec.marktype]); 31 | }); 32 | }; 33 | 34 | function deleteNulls(spec) { 35 | for (var i in spec) { 36 | if (_.isObject(spec[i])) { 37 | deleteNulls(spec[i]); 38 | } 39 | // This is why I hate js 40 | if (spec[i] === null || 41 | spec[i] === undefined || 42 | (_.isObject(spec[i]) && vl.keys(spec[i]).length === 0) || 43 | spec[i] === []) { 44 | delete spec[i]; 45 | } 46 | } 47 | } 48 | 49 | Spec.parseShorthand = function(newShorthand) { 50 | var newSpec = vl.Encoding.parseShorthand(newShorthand, Config.config).toSpec(); 51 | Spec.parseSpec(newSpec); 52 | }; 53 | 54 | // takes a partial spec 55 | Spec.parseSpec = function(newSpec) { 56 | Spec.spec = vl.schema.util.merge(Spec.instantiate(), newSpec); 57 | }; 58 | 59 | Spec.instantiate = function() { 60 | var spec = vl.schema.instantiate(); 61 | 62 | // we need to set the marktype because it doesn't have a default. 63 | spec.marktype = vl.schema.schema.properties.marktype.enum[0]; 64 | spec.config = Config.config; 65 | spec.data = Config.data; 66 | return spec; 67 | }; 68 | 69 | Spec.reset = function() { 70 | Spec.spec = Spec.instantiate(); 71 | }; 72 | 73 | // takes a full spec, validates it and then rebuilds everything 74 | Spec.update = function(spec) { 75 | spec = _.cloneDeep(spec || Spec.spec); 76 | 77 | Spec._removeEmptyFieldDefs(spec); 78 | deleteNulls(spec); 79 | 80 | // we may have removed enc 81 | if (!('encoding' in spec)) { 82 | spec.encoding = {}; 83 | } 84 | var validator = new ZSchema(); 85 | 86 | validator.setRemoteReference('http://json-schema.org/draft-04/schema', {}); 87 | 88 | var schema = vl.schema.schema; 89 | // now validate the spec 90 | var valid = validator.validate(spec, schema); 91 | 92 | if (!valid) { 93 | //FIXME: move this dependency to directive/controller layer 94 | Alerts.add({ 95 | msg: validator.getLastErrors() 96 | }); 97 | } else { 98 | vl.extend(spec.config, Config.large()); 99 | var encoding = vl.Encoding.fromSpec(spec), 100 | chart = Spec.chart; 101 | 102 | chart.fieldSet = Spec.spec.encoding; 103 | chart.vlSpec = spec; 104 | chart.cleanSpec = encoding.toSpec(false); 105 | chart.shorthand = encoding.toShorthand(); 106 | console.log('chart', chart.vgSpec, chart.vlSpec); 107 | } 108 | }; 109 | 110 | Spec.reset(); 111 | Dataset.onUpdate.push(Spec.reset); 112 | 113 | return Spec; 114 | }); 115 | -------------------------------------------------------------------------------- /src/app/pills/pills.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc service 5 | * @name polestar.Pills 6 | * @description 7 | * # Pills 8 | * Service in the polestar. 9 | */ 10 | angular.module('polestar') 11 | .service('Pills', function (vl, Spec, _, $window) { 12 | var encSchemaProps = vl.schema.schema.properties.encoding.properties; 13 | 14 | function instantiate(encType) { 15 | return vl.schema.util.instantiate(encSchemaProps[encType]); 16 | } 17 | 18 | var Pills = { 19 | pills: {} 20 | }; 21 | 22 | Pills.getSchemaPill = function(field) { 23 | return { 24 | name: field.name, 25 | type: field.type, 26 | aggregate: field.aggregate 27 | }; 28 | }; 29 | 30 | 31 | /** copy value from the pill to the fieldDef */ 32 | function updateFieldDef(enc, pill, encType){ 33 | var type = pill.type, 34 | supportedRole = vl.schema.getSupportedRole(encType), 35 | dimensionOnly = supportedRole.dimension && !supportedRole.measure; 36 | 37 | // auto cast binning / time binning for dimension only encoding type. 38 | if (pill.name && dimensionOnly) { 39 | if (pill.aggregate==='count') { 40 | pill = {}; 41 | $window.alert('COUNT not supported here!'); 42 | } else if (type==='Q' && !pill.bin) { 43 | pill.aggregate = undefined; 44 | pill.bin = {maxbins: vl.schema.MAXBINS_DEFAULT}; 45 | } else if(type==='T' && !pill.timeUnit) { 46 | pill.timeUnit = vl.schema.defaultTimeFn; 47 | } 48 | } else if (!pill.name) { 49 | // no name, it's actually the empty shelf that 50 | // got processed in the opposite direction 51 | pill = {}; 52 | } 53 | 54 | // filter unsupported properties 55 | var base = instantiate(encType), 56 | shelfProps = encSchemaProps[encType].properties; 57 | // console.log('updateFieldDef', encType, base, '<-', pill); 58 | for (var prop in shelfProps) { 59 | if (pill[prop]) { 60 | if (prop==='value' && pill.name) { 61 | // only copy value if name is not defined 62 | // (which should never be the case) 63 | delete base[prop]; 64 | } else { 65 | //FXIME In some case this should be merge / recursive merge instead ? 66 | base[prop] = pill[prop]; 67 | } 68 | } 69 | } 70 | enc[encType] = base; 71 | } 72 | 73 | Pills.remove = function (encType) { 74 | delete Pills.pills[encType]; 75 | updateFieldDef(Spec.spec.encoding, {}, encType); // remove all pill detail from the fieldDef 76 | }; 77 | 78 | Pills.update = function (encType) { 79 | updateFieldDef(Spec.spec.encoding, Pills.pills[encType], encType); 80 | }; 81 | 82 | Pills.dragStart = function (pill, encType) { 83 | Pills.pills.dragging = pill; 84 | Pills.pills.etDragFrom = encType; 85 | }; 86 | 87 | Pills.dragStop = function () { 88 | delete Pills.pills.dragging; 89 | }; 90 | 91 | Pills.dragDrop = function (etDragTo) { 92 | var enc = _.clone(Spec.spec.encoding), 93 | etDragFrom = Pills.pills.etDragFrom; 94 | // update the clone of the enc 95 | // console.log('dragDrop', enc, Pills, 'from:', etDragFrom, Pills.pills[etDragFrom]); 96 | if(etDragFrom){ 97 | // if pill is dragged from another shelf, not the schemalist 98 | // 99 | // console.log('pillDragFrom', Pills.pills[etDragFrom]); 100 | updateFieldDef(enc, Pills.pills[etDragFrom] || {}, etDragFrom); 101 | } 102 | updateFieldDef(enc, Pills.pills[etDragTo] || {}, etDragTo); 103 | 104 | // console.log('Pills.dragDrop', 105 | // 'from:', etDragFrom, Pills.pills[etDragFrom], enc[etDragFrom], 106 | // 'to:', etDragTo, Pills.pills[etDragTo], enc[etDragTo]); 107 | 108 | // Finally, update the enc only once to prevent glitches 109 | Spec.spec.encoding = enc; 110 | etDragFrom = null; 111 | }; 112 | 113 | return Pills; 114 | }); 115 | -------------------------------------------------------------------------------- /src/components/functionselect/functionselect.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('polestar') 4 | .directive('functionSelect', function(_, vl, Pills, Logger) { 5 | return { 6 | templateUrl: 'components/functionselect/functionselect.html', 7 | restrict: 'E', 8 | scope: { 9 | encType: '=', 10 | schema: '=', 11 | field: '=' 12 | }, 13 | link: function(scope /*,element, attrs*/) { 14 | var BIN='bin', RAW='', COUNT='count', maxbins; 15 | 16 | scope.pills = Pills.pills; 17 | scope.func = { 18 | selected: RAW, 19 | list: [RAW] 20 | }; 21 | 22 | function fieldPill() { 23 | return Pills ? Pills.pills[scope.encType] : null; 24 | } 25 | 26 | function getFns(type) { 27 | var schema = (scope.schema || {}).properties; 28 | if (schema && schema.timeUnit && (!schema.timeUnit.supportedTypes || schema.timeUnit.supportedTypes[type])) { 29 | return (schema.timeUnit.supportedEnums ? schema.timeUnit.supportedEnums[type] : schema.timeUnit.enum) || []; 30 | } 31 | return []; 32 | } 33 | 34 | function getAggrs(type) { 35 | if(!type) { 36 | return [COUNT]; 37 | } 38 | 39 | var schema = scope.schema.properties; 40 | 41 | if (schema && schema.aggregate && (!schema.aggregate.supportedTypes || schema.aggregate.supportedTypes[type])){ 42 | return (schema.aggregate.supportedEnums ? schema.aggregate.supportedEnums[type] : schema.aggregate.enum) || []; 43 | } 44 | return []; 45 | } 46 | 47 | scope.selectChanged = function() { 48 | Logger.logInteraction(Logger.actions.FUNC_CHANGE, scope.func.selected); 49 | }; 50 | 51 | // FIXME func.selected logic should be all moved to selectChanged 52 | // when the function select is updated, propagates change the parent 53 | scope.$watch('func.selected', function(selectedFunc) { 54 | var oldPill = fieldPill(), 55 | pill = _.clone(oldPill), 56 | type = pill ? pill.type : ''; 57 | 58 | if(!pill){ 59 | return; // not ready 60 | } 61 | 62 | // reset field def 63 | // HACK: we're temporarily storing the maxbins in the pill 64 | pill.bin = selectedFunc === BIN ? {maxbins: maxbins || vl.schema.MAXBINS_DEFAULT} : undefined; 65 | pill.aggregate = getAggrs(type).indexOf(selectedFunc) !== -1 ? selectedFunc : undefined; 66 | pill.timeUnit = getFns(type).indexOf(selectedFunc) !== -1 ? selectedFunc : undefined; 67 | 68 | if(!_.isEqual(oldPill, pill)){ 69 | Pills.pills[scope.encType] = pill; 70 | Pills.update(scope.encType); 71 | } 72 | }); 73 | 74 | // when parent objects modify the field 75 | scope.$watch('field', function(pill) { 76 | // only run this if schema is not null 77 | if (!scope.schema || !pill) { 78 | return; 79 | } 80 | 81 | var type = pill.name ? pill.type : ''; 82 | var schema = scope.schema.properties; 83 | 84 | // hack: save the maxbins 85 | if (pill.bin) { 86 | maxbins = pill.bin.maxbins; 87 | } 88 | 89 | var isOrdinalShelf = ['row','col','shape'].indexOf(scope.encType) !== -1, 90 | isQ = type==='Q', isT = type==='T'; 91 | 92 | if(pill.name==='*' && pill.aggregate===COUNT){ 93 | scope.func.list=[COUNT]; 94 | scope.func.selected = COUNT; 95 | } else { 96 | scope.func.list = ( isOrdinalShelf && (isQ||isT) ? [] : ['']) 97 | .concat(getFns(type)) 98 | .concat(getAggrs(type).filter(function(x) { return x !== COUNT; })) 99 | .concat(schema.bin && schema.bin.supportedTypes[type] ? ['bin'] : []); 100 | 101 | var defaultVal = (isOrdinalShelf && 102 | (isQ && BIN) || (isT && vl.schema.defaultTimeFn) 103 | )|| RAW; 104 | 105 | var selected = pill.bin ? 'bin' : 106 | pill.aggregate || pill.timeUnit || 107 | defaultVal; 108 | 109 | if (scope.func.list.indexOf(selected) >= 0) { 110 | scope.func.selected = selected; 111 | } else { 112 | scope.func.selected = defaultVal; 113 | } 114 | 115 | } 116 | }, true); 117 | } 118 | }; 119 | }); 120 | -------------------------------------------------------------------------------- /gulp/gen.js: -------------------------------------------------------------------------------- 1 | /** 2 | * script for generating angular primitives 3 | * 4 | * gulp gen -d directiveName (or --directive) 5 | * gulp gen -s serviceName (or --service) 6 | * gulp gen -c controllerName (or --controller) 7 | * gulp gen --fi filterName (or --filter) 8 | * gulp gen --fa factoryName (or --factory) 9 | */ 10 | 11 | 'use strict'; 12 | 13 | var gulp = require('gulp'), 14 | argv = require('yargs').argv, 15 | fs = require('fs'), 16 | request = require('request'), 17 | pack = require('../package.json'); 18 | 19 | var APP_NAME = pack.name; 20 | var SRC_PATH = 'src/'; 21 | var APP_PATH = 'app/'; 22 | var COMP_PATH = 'components/'; 23 | 24 | var GIST_URL = 'https://gist.githubusercontent.com/kanitw/15256571933310366d00/raw/'; 25 | 26 | function createFile(path){ 27 | fs.closeSync(fs.openSync(path, 'w')); 28 | } 29 | 30 | function fetchUrl(url, callback){ 31 | console.log('fetching', url); 32 | request.get(url, function (error, response, body) { 33 | if (!error && response.statusCode === 200) { 34 | callback(body); 35 | } 36 | }) 37 | .on('error', function(err){ 38 | console.log(err); 39 | }); 40 | } 41 | 42 | function replace(str, template){ 43 | for(var key in template){ 44 | str = str.replace(new RegExp(key, 'g'), template[key]); 45 | } 46 | return str; 47 | } 48 | 49 | function getAppName() { 50 | return argv.a || argv.appname || APP_NAME; 51 | } 52 | 53 | function genDirective(dir){ 54 | var ldir = dir.toLowerCase(), 55 | dirpath = SRC_PATH + COMP_PATH + ldir +'/', 56 | dirdash = dir.replace(/([A-Z])/g, ' $1') // insert a space before all caps 57 | .toLowerCase() 58 | .split(' ') 59 | .join('-'); 60 | 61 | if(! fs.existsSync(dirpath)){ 62 | fs.mkdirSync(dirpath); 63 | } 64 | // create template, scss 65 | createFile(dirpath+ ldir +'.scss'); 66 | fs.writeFileSync(dirpath + ldir + '.html' , '
'); 67 | 68 | // create directive file 69 | fetchUrl(GIST_URL + 'directive.js', function(str) { 70 | fs.writeFileSync(dirpath + ldir + '.js' , replace(str, { 71 | __appname__: getAppName(), 72 | __directive__: dir, 73 | __component__dir__: COMP_PATH, 74 | '__directive_lower__': ldir, 75 | '__directive_dash__': dirdash 76 | })); 77 | }); 78 | 79 | // create spec file 80 | fetchUrl(GIST_URL + 'directive.spec.js', function(str) { 81 | fs.writeFileSync(dirpath + ldir + '.spec.js' , replace(str, { 82 | __appname__: getAppName(), 83 | __directive__: dir, 84 | '__directive_lower__': ldir, 85 | '__directive_dash__': dirdash 86 | })); 87 | }); 88 | } 89 | 90 | function genItem(rootpath, item, fileurl, specurl, itemtype){ 91 | var filename = item.toLowerCase(), 92 | dirpath = SRC_PATH + rootpath + filename +'/', 93 | template = { __appname__: getAppName() }; 94 | 95 | if(! fs.existsSync(dirpath)){ 96 | fs.mkdirSync(dirpath); 97 | } 98 | 99 | template['__'+itemtype+'__'] = item; 100 | 101 | // create directive file 102 | fetchUrl(fileurl, function(str) { 103 | fs.writeFileSync(dirpath + filename + '.' + itemtype + '.js' , replace(str, template)); 104 | }); 105 | 106 | // create spec file 107 | fetchUrl(specurl, function(str) { 108 | fs.writeFileSync(dirpath + filename + '.' + itemtype + '.spec.js' , replace(str, template)); 109 | }); 110 | } 111 | 112 | function genService(srv){ 113 | genItem(APP_PATH, srv, GIST_URL + 'service.js', 114 | GIST_URL + 'service.spec.js', 'service'); 115 | } 116 | 117 | function genFilter(f){ 118 | genItem(COMP_PATH, f, GIST_URL + 'filter.js', GIST_URL + 'filter.spec.js', 'filter'); 119 | } 120 | 121 | function genFactory(f){ 122 | genItem(APP_PATH, f, GIST_URL + 'factory.js', GIST_URL + 'factory.spec.js', 'factory'); 123 | } 124 | 125 | function genController(c){ 126 | // controller name should be title case (Ctrl suffix is already appended in the template) 127 | var Cc = c.substr(0,1).toUpperCase() + c.substr(1); 128 | 129 | genItem(APP_PATH, Cc, GIST_URL + 'controller.js', GIST_URL + 'controller.spec.js', 'controller'); 130 | } 131 | 132 | gulp.task('gen', function() { 133 | if (argv.d || argv.directive) { 134 | genDirective(argv.d || argv.directive); 135 | } else if (argv.s || argv.service) { 136 | genService(argv.s || argv.service); 137 | } else if (argv.c || argv.controller) { 138 | genController(argv.c || argv.controller); 139 | } else if (argv.fi || argv.filter) { 140 | genFilter(argv.fi || argv.filter); 141 | } else if (argv.fa || argv.factory) { 142 | genFactory(argv.fa || argv.factory); 143 | } else { 144 | console.log('please supply flag to generate an angular object you want'); 145 | } 146 | }); 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pole✭ (Polestar) -- Alpha 2 | 3 | [![Build Status](https://travis-ci.org/uwdata/polestar.svg)](https://travis-ci.org/uwdata/polestar) 4 | 5 | PoleStar is Tableau-style User Interface for visual analysis, building on top 6 | of [Vega-lite](https://github.com/uwdata/vega-lite). Try our [online 7 | demo](http://uwdata.github.io/polestar/). Also, be sure to check out [related 8 | projects](https://vega.github.io/). 9 | 10 | **This project is an [alpha](http://en.wikipedia.org/wiki/Software_release_life_cycle#Alpha) software. 11 | We are working on improving its code and documentation.** 12 | 13 | If you are using Polestar for your project(s), please let us know what are you using it for by emailing us at [Vega-lite \[at\] cs.washington.edu](mailto:vega-lite@cs.washington.edu). Feedbacks are also welcomed. 14 | If you find a bug or have a feature request, please [create an issue](https://github.com/uwdata/polestar/issues/new). 15 | 16 | ## Team 17 | 18 | Polestar's development is led by Dominik Moritz, Kanit Wongsuphasawat, and Jeffrey Heer at the University of Washington [Interactive Data Lab](http://idl.cs.washington.edu), in collaboration with [UW eScience Institute](http://escience.washington.edu/) and [Tableau Research](http://research.tableau.com) 19 | 20 | ## Setup Instruction 21 | 22 | ### Install Dependencies 23 | 24 | Make sure you have node.js. (We recommend using [homebrew](http://brew.sh) and simply run `brew install node`.) 25 | 26 | Install gulp + bower globally by running 27 | 28 | ```sh 29 | npm install -g bower 30 | npm install -g gulp 31 | ``` 32 | 33 | Then install all the npm, bower dependencies: 34 | 35 | ```sh 36 | npm install 37 | bower install 38 | ``` 39 | 40 | Now you should have all dependencies and should be ready to work. 41 | 42 | ### Running 43 | 44 | You can run `gulp serve`, which serves the site as well as running tests in the background. 45 | If you edit any file, our gulp task runner should automatically refresh the browser and re-run tests. 46 | 47 | ## Development Guide 48 | 49 | ### Folder Structure 50 | 51 | We try to follow [Google's Angular Best Practice for Angular App Structure](https://docs.google.com/document/d/1XXMvReO8-Awi1EZXAXS4PzDzdNvV6pGcuaF4Q9821Es/pub) and use [generator-gulp-angular](https://github.com/Swiip/generator-gulp-angular) to setup the project. 52 | 53 | All source code are under `src/` 54 | 55 | - `src/app/` contains our main classes 56 | - `src/components` contains our other components 57 | - `src/assets/images/` contains relevant images 58 | - `src/data/` contains all data that we use in the application 59 | - `src/vendor` contains 60 | 61 | @kanitw created [`gulp/gen.js`](https://github.com/uwdata/polestar/blob/master/gulp/gen.js) for help generating angular components. 62 | For example, you can run `gulp gen -d directiveName` and this would create all relevant files including the javascript file, the template file, the stylesheet file and the test spec. 63 | 64 | #### Coding Style 65 | 66 | We use jshint as our linter for coding in the project. 67 | 68 | #### Stylesheets 69 | 70 | We use [sass](http://sass-lang.com) as it is a better syntax for css. 71 | 72 | #### Dependencies 73 | 74 | This project depends on [Datalib](https://github.com/uwdata/datalib) for data processing, [Vega-lite](https://github.com/uwdata/vega-lite) as a formal model for visualization, and [Vega-lite-ui](https://github.com/uwdata/vega-lite-ui), which contains shared components between Polestar and Voyager. 75 | 76 | If you plan to make changes to these dependencies and observe the changes without publishing / copying compiled libraries all the time, use [`bower link`](https://oncletom.io/2013/live-development-bower-component/). 77 | 78 | In each of your dependency repository, run 79 | 80 | ``` 81 | cd path/to/dependency-repo 82 | bower link 83 | ``` 84 | 85 | Then go to this project's directory and run 86 | 87 | ``` 88 | bower link datalib 89 | bower link vega-lite 90 | bower link vega-lite-ui 91 | ``` 92 | 93 | Now all the changes you make in each repo will be reflected in your Vega-lite automatically. 94 | 95 | Since bower uses the compiled main file, make sure that each repos is compiled everytime you run `gulp serve`. 96 | Otherwise, you will get errors for missing libraries. 97 | 98 | ### Releasing / Github Pages 99 | 100 | `gh-pages` branch is for releasing a stable version. 101 | `gh-pages` should only contain the dist folder. 102 | 103 | Use `publish.sh` to: 104 | 105 | 1. publish the current version to npm 106 | 2. deploy the current branch to gh-pages and 107 | 3. create a release tag for github and bower. 108 | 109 | 110 | ## Acknowledgement 111 | 112 | We used [generator-gulp-angular](https://github.com/Swiip/generator-gulp-angular) for bootstraping our project. 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/data/barley.json: -------------------------------------------------------------------------------- 1 | [{"yield":27,"variety":"Manchuria","year":1931,"site":"University Farm"}, 2 | {"yield":48.86667,"variety":"Manchuria","year":1931,"site":"Waseca"}, 3 | {"yield":27.43334,"variety":"Manchuria","year":1931,"site":"Morris"}, 4 | {"yield":39.93333,"variety":"Manchuria","year":1931,"site":"Crookston"}, 5 | {"yield":32.96667,"variety":"Manchuria","year":1931,"site":"Grand Rapids"}, 6 | {"yield":28.96667,"variety":"Manchuria","year":1931,"site":"Duluth"}, 7 | {"yield":43.06666,"variety":"Glabron","year":1931,"site":"University Farm"}, 8 | {"yield":55.2,"variety":"Glabron","year":1931,"site":"Waseca"}, 9 | {"yield":28.76667,"variety":"Glabron","year":1931,"site":"Morris"}, 10 | {"yield":38.13333,"variety":"Glabron","year":1931,"site":"Crookston"}, 11 | {"yield":29.13333,"variety":"Glabron","year":1931,"site":"Grand Rapids"}, 12 | {"yield":29.66667,"variety":"Glabron","year":1931,"site":"Duluth"}, 13 | {"yield":35.13333,"variety":"Svansota","year":1931,"site":"University Farm"}, 14 | {"yield":47.33333,"variety":"Svansota","year":1931,"site":"Waseca"}, 15 | {"yield":25.76667,"variety":"Svansota","year":1931,"site":"Morris"}, 16 | {"yield":40.46667,"variety":"Svansota","year":1931,"site":"Crookston"}, 17 | {"yield":29.66667,"variety":"Svansota","year":1931,"site":"Grand Rapids"}, 18 | {"yield":25.7,"variety":"Svansota","year":1931,"site":"Duluth"}, 19 | {"yield":39.9,"variety":"Velvet","year":1931,"site":"University Farm"}, 20 | {"yield":50.23333,"variety":"Velvet","year":1931,"site":"Waseca"}, 21 | {"yield":26.13333,"variety":"Velvet","year":1931,"site":"Morris"}, 22 | {"yield":41.33333,"variety":"Velvet","year":1931,"site":"Crookston"}, 23 | {"yield":23.03333,"variety":"Velvet","year":1931,"site":"Grand Rapids"}, 24 | {"yield":26.3,"variety":"Velvet","year":1931,"site":"Duluth"}, 25 | {"yield":36.56666,"variety":"Trebi","year":1931,"site":"University Farm"}, 26 | {"yield":63.8333,"variety":"Trebi","year":1931,"site":"Waseca"}, 27 | {"yield":43.76667,"variety":"Trebi","year":1931,"site":"Morris"}, 28 | {"yield":46.93333,"variety":"Trebi","year":1931,"site":"Crookston"}, 29 | {"yield":29.76667,"variety":"Trebi","year":1931,"site":"Grand Rapids"}, 30 | {"yield":33.93333,"variety":"Trebi","year":1931,"site":"Duluth"}, 31 | {"yield":43.26667,"variety":"No. 457","year":1931,"site":"University Farm"}, 32 | {"yield":58.1,"variety":"No. 457","year":1931,"site":"Waseca"}, 33 | {"yield":28.7,"variety":"No. 457","year":1931,"site":"Morris"}, 34 | {"yield":45.66667,"variety":"No. 457","year":1931,"site":"Crookston"}, 35 | {"yield":32.16667,"variety":"No. 457","year":1931,"site":"Grand Rapids"}, 36 | {"yield":33.6,"variety":"No. 457","year":1931,"site":"Duluth"}, 37 | {"yield":36.6,"variety":"No. 462","year":1931,"site":"University Farm"}, 38 | {"yield":65.7667,"variety":"No. 462","year":1931,"site":"Waseca"}, 39 | {"yield":30.36667,"variety":"No. 462","year":1931,"site":"Morris"}, 40 | {"yield":48.56666,"variety":"No. 462","year":1931,"site":"Crookston"}, 41 | {"yield":24.93334,"variety":"No. 462","year":1931,"site":"Grand Rapids"}, 42 | {"yield":28.1,"variety":"No. 462","year":1931,"site":"Duluth"}, 43 | {"yield":32.76667,"variety":"Peatland","year":1931,"site":"University Farm"}, 44 | {"yield":48.56666,"variety":"Peatland","year":1931,"site":"Waseca"}, 45 | {"yield":29.86667,"variety":"Peatland","year":1931,"site":"Morris"}, 46 | {"yield":41.6,"variety":"Peatland","year":1931,"site":"Crookston"}, 47 | {"yield":34.7,"variety":"Peatland","year":1931,"site":"Grand Rapids"}, 48 | {"yield":32,"variety":"Peatland","year":1931,"site":"Duluth"}, 49 | {"yield":24.66667,"variety":"No. 475","year":1931,"site":"University Farm"}, 50 | {"yield":46.76667,"variety":"No. 475","year":1931,"site":"Waseca"}, 51 | {"yield":22.6,"variety":"No. 475","year":1931,"site":"Morris"}, 52 | {"yield":44.1,"variety":"No. 475","year":1931,"site":"Crookston"}, 53 | {"yield":19.7,"variety":"No. 475","year":1931,"site":"Grand Rapids"}, 54 | {"yield":33.06666,"variety":"No. 475","year":1931,"site":"Duluth"}, 55 | {"yield":39.3,"variety":"Wisconsin No. 38","year":1931,"site":"University Farm"}, 56 | {"yield":58.8,"variety":"Wisconsin No. 38","year":1931,"site":"Waseca"}, 57 | {"yield":29.46667,"variety":"Wisconsin No. 38","year":1931,"site":"Morris"}, 58 | {"yield":49.86667,"variety":"Wisconsin No. 38","year":1931,"site":"Crookston"}, 59 | {"yield":34.46667,"variety":"Wisconsin No. 38","year":1931,"site":"Grand Rapids"}, 60 | {"yield":31.6,"variety":"Wisconsin No. 38","year":1931,"site":"Duluth"}, 61 | {"yield":26.9,"variety":"Manchuria","year":1932,"site":"University Farm"}, 62 | {"yield":33.46667,"variety":"Manchuria","year":1932,"site":"Waseca"}, 63 | {"yield":34.36666,"variety":"Manchuria","year":1932,"site":"Morris"}, 64 | {"yield":32.96667,"variety":"Manchuria","year":1932,"site":"Crookston"}, 65 | {"yield":22.13333,"variety":"Manchuria","year":1932,"site":"Grand Rapids"}, 66 | {"yield":22.56667,"variety":"Manchuria","year":1932,"site":"Duluth"}, 67 | {"yield":36.8,"variety":"Glabron","year":1932,"site":"University Farm"}, 68 | {"yield":37.73333,"variety":"Glabron","year":1932,"site":"Waseca"}, 69 | {"yield":35.13333,"variety":"Glabron","year":1932,"site":"Morris"}, 70 | {"yield":26.16667,"variety":"Glabron","year":1932,"site":"Crookston"}, 71 | {"yield":14.43333,"variety":"Glabron","year":1932,"site":"Grand Rapids"}, 72 | {"yield":25.86667,"variety":"Glabron","year":1932,"site":"Duluth"}, 73 | {"yield":27.43334,"variety":"Svansota","year":1932,"site":"University Farm"}, 74 | {"yield":38.5,"variety":"Svansota","year":1932,"site":"Waseca"}, 75 | {"yield":35.03333,"variety":"Svansota","year":1932,"site":"Morris"}, 76 | {"yield":20.63333,"variety":"Svansota","year":1932,"site":"Crookston"}, 77 | {"yield":16.63333,"variety":"Svansota","year":1932,"site":"Grand Rapids"}, 78 | {"yield":22.23333,"variety":"Svansota","year":1932,"site":"Duluth"}, 79 | {"yield":26.8,"variety":"Velvet","year":1932,"site":"University Farm"}, 80 | {"yield":37.4,"variety":"Velvet","year":1932,"site":"Waseca"}, 81 | {"yield":38.83333,"variety":"Velvet","year":1932,"site":"Morris"}, 82 | {"yield":32.06666,"variety":"Velvet","year":1932,"site":"Crookston"}, 83 | {"yield":32.23333,"variety":"Velvet","year":1932,"site":"Grand Rapids"}, 84 | {"yield":22.46667,"variety":"Velvet","year":1932,"site":"Duluth"}, 85 | {"yield":29.06667,"variety":"Trebi","year":1932,"site":"University Farm"}, 86 | {"yield":49.2333,"variety":"Trebi","year":1932,"site":"Waseca"}, 87 | {"yield":46.63333,"variety":"Trebi","year":1932,"site":"Morris"}, 88 | {"yield":41.83333,"variety":"Trebi","year":1932,"site":"Crookston"}, 89 | {"yield":20.63333,"variety":"Trebi","year":1932,"site":"Grand Rapids"}, 90 | {"yield":30.6,"variety":"Trebi","year":1932,"site":"Duluth"}, 91 | {"yield":26.43334,"variety":"No. 457","year":1932,"site":"University Farm"}, 92 | {"yield":42.2,"variety":"No. 457","year":1932,"site":"Waseca"}, 93 | {"yield":43.53334,"variety":"No. 457","year":1932,"site":"Morris"}, 94 | {"yield":34.33333,"variety":"No. 457","year":1932,"site":"Crookston"}, 95 | {"yield":19.46667,"variety":"No. 457","year":1932,"site":"Grand Rapids"}, 96 | {"yield":22.7,"variety":"No. 457","year":1932,"site":"Duluth"}, 97 | {"yield":25.56667,"variety":"No. 462","year":1932,"site":"University Farm"}, 98 | {"yield":44.7,"variety":"No. 462","year":1932,"site":"Waseca"}, 99 | {"yield":47,"variety":"No. 462","year":1932,"site":"Morris"}, 100 | {"yield":30.53333,"variety":"No. 462","year":1932,"site":"Crookston"}, 101 | {"yield":19.9,"variety":"No. 462","year":1932,"site":"Grand Rapids"}, 102 | {"yield":22.5,"variety":"No. 462","year":1932,"site":"Duluth"}, 103 | {"yield":28.06667,"variety":"Peatland","year":1932,"site":"University Farm"}, 104 | {"yield":36.03333,"variety":"Peatland","year":1932,"site":"Waseca"}, 105 | {"yield":43.2,"variety":"Peatland","year":1932,"site":"Morris"}, 106 | {"yield":25.23333,"variety":"Peatland","year":1932,"site":"Crookston"}, 107 | {"yield":26.76667,"variety":"Peatland","year":1932,"site":"Grand Rapids"}, 108 | {"yield":31.36667,"variety":"Peatland","year":1932,"site":"Duluth"}, 109 | {"yield":30,"variety":"No. 475","year":1932,"site":"University Farm"}, 110 | {"yield":41.26667,"variety":"No. 475","year":1932,"site":"Waseca"}, 111 | {"yield":44.23333,"variety":"No. 475","year":1932,"site":"Morris"}, 112 | {"yield":32.13333,"variety":"No. 475","year":1932,"site":"Crookston"}, 113 | {"yield":15.23333,"variety":"No. 475","year":1932,"site":"Grand Rapids"}, 114 | {"yield":27.36667,"variety":"No. 475","year":1932,"site":"Duluth"}, 115 | {"yield":38,"variety":"Wisconsin No. 38","year":1932,"site":"University Farm"}, 116 | {"yield":58.16667,"variety":"Wisconsin No. 38","year":1932,"site":"Waseca"}, 117 | {"yield":47.16667,"variety":"Wisconsin No. 38","year":1932,"site":"Morris"}, 118 | {"yield":35.9,"variety":"Wisconsin No. 38","year":1932,"site":"Crookston"}, 119 | {"yield":20.66667,"variety":"Wisconsin No. 38","year":1932,"site":"Grand Rapids"}, 120 | {"yield":29.33333,"variety":"Wisconsin No. 38","year":1932,"site":"Duluth"}] -------------------------------------------------------------------------------- /src/assets/normalize.scss: -------------------------------------------------------------------------------- 1 | // copied directly from normalize.css 2 | 3 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 4 | 5 | /** 6 | * 1. Set default font family to sans-serif. 7 | * 2. Prevent iOS text size adjust after orientation change, without disabling 8 | * user zoom. 9 | */ 10 | 11 | html { 12 | font-family: sans-serif; /* 1 */ 13 | -ms-text-size-adjust: 100%; /* 2 */ 14 | -webkit-text-size-adjust: 100%; /* 2 */ 15 | } 16 | 17 | /** 18 | * Remove default margin. 19 | */ 20 | 21 | body { 22 | margin: 0; 23 | } 24 | 25 | /* HTML5 display definitions 26 | ========================================================================== */ 27 | 28 | /** 29 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 30 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 31 | * and Firefox. 32 | * Correct `block` display not defined for `main` in IE 11. 33 | */ 34 | 35 | article, 36 | aside, 37 | details, 38 | figcaption, 39 | figure, 40 | footer, 41 | header, 42 | hgroup, 43 | main, 44 | menu, 45 | nav, 46 | section, 47 | summary { 48 | display: block; 49 | } 50 | 51 | /** 52 | * 1. Correct `inline-block` display not defined in IE 8/9. 53 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 54 | */ 55 | 56 | audio, 57 | canvas, 58 | progress, 59 | video { 60 | display: inline-block; /* 1 */ 61 | vertical-align: baseline; /* 2 */ 62 | } 63 | 64 | /** 65 | * Prevent modern browsers from displaying `audio` without controls. 66 | * Remove excess height in iOS 5 devices. 67 | */ 68 | 69 | audio:not([controls]) { 70 | display: none; 71 | height: 0; 72 | } 73 | 74 | /** 75 | * Address `[hidden]` styling not present in IE 8/9/10. 76 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 77 | */ 78 | 79 | [hidden], 80 | template { 81 | display: none; 82 | } 83 | 84 | /* Links 85 | ========================================================================== */ 86 | 87 | /** 88 | * Remove the gray background color from active links in IE 10. 89 | */ 90 | 91 | a { 92 | background-color: transparent; 93 | } 94 | 95 | /** 96 | * Improve readability when focused and also mouse hovered in all browsers. 97 | */ 98 | 99 | a:active, 100 | a:hover { 101 | outline: 0; 102 | } 103 | 104 | /* Text-level semantics 105 | ========================================================================== */ 106 | 107 | /** 108 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 109 | */ 110 | 111 | abbr[title] { 112 | border-bottom: 1px dotted; 113 | } 114 | 115 | /** 116 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 117 | */ 118 | 119 | b, 120 | strong { 121 | font-weight: bold; 122 | } 123 | 124 | /** 125 | * Address styling not present in Safari and Chrome. 126 | */ 127 | 128 | dfn { 129 | font-style: italic; 130 | } 131 | 132 | /** 133 | * Address variable `h1` font-size and margin within `section` and `article` 134 | * contexts in Firefox 4+, Safari, and Chrome. 135 | */ 136 | 137 | h1 { 138 | font-size: 2em; 139 | margin: 0.67em 0; 140 | } 141 | 142 | /** 143 | * Address styling not present in IE 8/9. 144 | */ 145 | 146 | mark { 147 | background: #ff0; 148 | color: #000; 149 | } 150 | 151 | /** 152 | * Address inconsistent and variable font size in all browsers. 153 | */ 154 | 155 | small { 156 | font-size: 80%; 157 | } 158 | 159 | /** 160 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 161 | */ 162 | 163 | sub, 164 | sup { 165 | font-size: 75%; 166 | line-height: 0; 167 | position: relative; 168 | vertical-align: baseline; 169 | } 170 | 171 | sup { 172 | top: -0.5em; 173 | } 174 | 175 | sub { 176 | bottom: -0.25em; 177 | } 178 | 179 | /* Embedded content 180 | ========================================================================== */ 181 | 182 | /** 183 | * Remove border when inside `a` element in IE 8/9/10. 184 | */ 185 | 186 | img { 187 | border: 0; 188 | } 189 | 190 | /** 191 | * Correct overflow not hidden in IE 9/10/11. 192 | */ 193 | 194 | svg:not(:root) { 195 | overflow: hidden; 196 | } 197 | 198 | /* Grouping content 199 | ========================================================================== */ 200 | 201 | /** 202 | * Address margin not present in IE 8/9 and Safari. 203 | */ 204 | 205 | figure { 206 | margin: 1em 40px; 207 | } 208 | 209 | /** 210 | * Address differences between Firefox and other browsers. 211 | */ 212 | 213 | hr { 214 | -moz-box-sizing: content-box; 215 | box-sizing: content-box; 216 | height: 0; 217 | } 218 | 219 | /** 220 | * Contain overflow in all browsers. 221 | */ 222 | 223 | pre { 224 | overflow: auto; 225 | } 226 | 227 | /** 228 | * Address odd `em`-unit font size rendering in all browsers. 229 | */ 230 | 231 | code, 232 | kbd, 233 | pre, 234 | samp { 235 | font-family: monospace, monospace; 236 | font-size: 1em; 237 | } 238 | 239 | /* Forms 240 | ========================================================================== */ 241 | 242 | /** 243 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 244 | * styling of `select`, unless a `border` property is set. 245 | */ 246 | 247 | /** 248 | * 1. Correct color not being inherited. 249 | * Known issue: affects color of disabled elements. 250 | * 2. Correct font properties not being inherited. 251 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 252 | */ 253 | 254 | button, 255 | input, 256 | optgroup, 257 | select, 258 | textarea { 259 | color: inherit; /* 1 */ 260 | font: inherit; /* 2 */ 261 | margin: 0; /* 3 */ 262 | } 263 | 264 | /** 265 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 266 | */ 267 | 268 | button { 269 | overflow: visible; 270 | } 271 | 272 | /** 273 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 274 | * All other form control elements do not inherit `text-transform` values. 275 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 276 | * Correct `select` style inheritance in Firefox. 277 | */ 278 | 279 | button, 280 | select { 281 | text-transform: none; 282 | } 283 | 284 | /** 285 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 286 | * and `video` controls. 287 | * 2. Correct inability to style clickable `input` types in iOS. 288 | * 3. Improve usability and consistency of cursor style between image-type 289 | * `input` and others. 290 | */ 291 | 292 | button, 293 | html input[type="button"], /* 1 */ 294 | input[type="reset"], 295 | input[type="submit"] { 296 | -webkit-appearance: button; /* 2 */ 297 | cursor: pointer; /* 3 */ 298 | } 299 | 300 | /** 301 | * Re-set default cursor for disabled elements. 302 | */ 303 | 304 | button[disabled], 305 | html input[disabled] { 306 | cursor: default; 307 | } 308 | 309 | /** 310 | * Remove inner padding and border in Firefox 4+. 311 | */ 312 | 313 | button::-moz-focus-inner, 314 | input::-moz-focus-inner { 315 | border: 0; 316 | padding: 0; 317 | } 318 | 319 | /** 320 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 321 | * the UA stylesheet. 322 | */ 323 | 324 | input { 325 | line-height: normal; 326 | } 327 | 328 | /** 329 | * It's recommended that you don't attempt to style these elements. 330 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 331 | * 332 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 333 | * 2. Remove excess padding in IE 8/9/10. 334 | */ 335 | 336 | input[type="checkbox"], 337 | input[type="radio"] { 338 | box-sizing: border-box; /* 1 */ 339 | padding: 0; /* 2 */ 340 | } 341 | 342 | /** 343 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 344 | * `font-size` values of the `input`, it causes the cursor style of the 345 | * decrement button to change from `default` to `text`. 346 | */ 347 | 348 | input[type="number"]::-webkit-inner-spin-button, 349 | input[type="number"]::-webkit-outer-spin-button { 350 | height: auto; 351 | } 352 | 353 | /** 354 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 355 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 356 | * (include `-moz` to future-proof). 357 | */ 358 | 359 | input[type="search"] { 360 | -webkit-appearance: textfield; /* 1 */ 361 | -moz-box-sizing: content-box; 362 | -webkit-box-sizing: content-box; /* 2 */ 363 | box-sizing: content-box; 364 | } 365 | 366 | /** 367 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 368 | * Safari (but not Chrome) clips the cancel button when the search input has 369 | * padding (and `textfield` appearance). 370 | */ 371 | 372 | input[type="search"]::-webkit-search-cancel-button, 373 | input[type="search"]::-webkit-search-decoration { 374 | -webkit-appearance: none; 375 | } 376 | 377 | /** 378 | * Define consistent border, margin, and padding. 379 | */ 380 | 381 | fieldset { 382 | border: 1px solid #c0c0c0; 383 | margin: 0 2px; 384 | padding: 0.35em 0.625em 0.75em; 385 | } 386 | 387 | /** 388 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 389 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 390 | */ 391 | 392 | legend { 393 | border: 0; /* 1 */ 394 | padding: 0; /* 2 */ 395 | } 396 | 397 | /** 398 | * Remove default vertical scrollbar in IE 8/9/10/11. 399 | */ 400 | 401 | textarea { 402 | overflow: auto; 403 | } 404 | 405 | /** 406 | * Don't inherit the `font-weight` (applied by a rule above). 407 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 408 | */ 409 | 410 | optgroup { 411 | font-weight: bold; 412 | } 413 | 414 | /* Tables 415 | ========================================================================== */ 416 | 417 | /** 418 | * Remove most spacing between table cells. 419 | */ 420 | 421 | table { 422 | border-collapse: collapse; 423 | border-spacing: 0; 424 | } 425 | 426 | td, 427 | th { 428 | padding: 0; 429 | } 430 | -------------------------------------------------------------------------------- /src/data/iris.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"sepalLength": 5.1, "sepalWidth": 3.5, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"}, 3 | {"sepalLength": 4.9, "sepalWidth": 3.0, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"}, 4 | {"sepalLength": 4.7, "sepalWidth": 3.2, "petalLength": 1.3, "petalWidth": 0.2, "species": "setosa"}, 5 | {"sepalLength": 4.6, "sepalWidth": 3.1, "petalLength": 1.5, "petalWidth": 0.2, "species": "setosa"}, 6 | {"sepalLength": 5.0, "sepalWidth": 3.6, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"}, 7 | {"sepalLength": 5.4, "sepalWidth": 3.9, "petalLength": 1.7, "petalWidth": 0.4, "species": "setosa"}, 8 | {"sepalLength": 4.6, "sepalWidth": 3.4, "petalLength": 1.4, "petalWidth": 0.3, "species": "setosa"}, 9 | {"sepalLength": 5.0, "sepalWidth": 3.4, "petalLength": 1.5, "petalWidth": 0.2, "species": "setosa"}, 10 | {"sepalLength": 4.4, "sepalWidth": 2.9, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"}, 11 | {"sepalLength": 4.9, "sepalWidth": 3.1, "petalLength": 1.5, "petalWidth": 0.1, "species": "setosa"}, 12 | {"sepalLength": 5.4, "sepalWidth": 3.7, "petalLength": 1.5, "petalWidth": 0.2, "species": "setosa"}, 13 | {"sepalLength": 4.8, "sepalWidth": 3.4, "petalLength": 1.6, "petalWidth": 0.2, "species": "setosa"}, 14 | {"sepalLength": 4.8, "sepalWidth": 3.0, "petalLength": 1.4, "petalWidth": 0.1, "species": "setosa"}, 15 | {"sepalLength": 4.3, "sepalWidth": 3.0, "petalLength": 1.1, "petalWidth": 0.1, "species": "setosa"}, 16 | {"sepalLength": 5.8, "sepalWidth": 4.0, "petalLength": 1.2, "petalWidth": 0.2, "species": "setosa"}, 17 | {"sepalLength": 5.7, "sepalWidth": 4.4, "petalLength": 1.5, "petalWidth": 0.4, "species": "setosa"}, 18 | {"sepalLength": 5.4, "sepalWidth": 3.9, "petalLength": 1.3, "petalWidth": 0.4, "species": "setosa"}, 19 | {"sepalLength": 5.1, "sepalWidth": 3.5, "petalLength": 1.4, "petalWidth": 0.3, "species": "setosa"}, 20 | {"sepalLength": 5.7, "sepalWidth": 3.8, "petalLength": 1.7, "petalWidth": 0.3, "species": "setosa"}, 21 | {"sepalLength": 5.1, "sepalWidth": 3.8, "petalLength": 1.5, "petalWidth": 0.3, "species": "setosa"}, 22 | {"sepalLength": 5.4, "sepalWidth": 3.4, "petalLength": 1.7, "petalWidth": 0.2, "species": "setosa"}, 23 | {"sepalLength": 5.1, "sepalWidth": 3.7, "petalLength": 1.5, "petalWidth": 0.4, "species": "setosa"}, 24 | {"sepalLength": 4.6, "sepalWidth": 3.6, "petalLength": 1.0, "petalWidth": 0.2, "species": "setosa"}, 25 | {"sepalLength": 5.1, "sepalWidth": 3.3, "petalLength": 1.7, "petalWidth": 0.5, "species": "setosa"}, 26 | {"sepalLength": 4.8, "sepalWidth": 3.4, "petalLength": 1.9, "petalWidth": 0.2, "species": "setosa"}, 27 | {"sepalLength": 5.0, "sepalWidth": 3.0, "petalLength": 1.6, "petalWidth": 0.2, "species": "setosa"}, 28 | {"sepalLength": 5.0, "sepalWidth": 3.4, "petalLength": 1.6, "petalWidth": 0.4, "species": "setosa"}, 29 | {"sepalLength": 5.2, "sepalWidth": 3.5, "petalLength": 1.5, "petalWidth": 0.2, "species": "setosa"}, 30 | {"sepalLength": 5.2, "sepalWidth": 3.4, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"}, 31 | {"sepalLength": 4.7, "sepalWidth": 3.2, "petalLength": 1.6, "petalWidth": 0.2, "species": "setosa"}, 32 | {"sepalLength": 4.8, "sepalWidth": 3.1, "petalLength": 1.6, "petalWidth": 0.2, "species": "setosa"}, 33 | {"sepalLength": 5.4, "sepalWidth": 3.4, "petalLength": 1.5, "petalWidth": 0.4, "species": "setosa"}, 34 | {"sepalLength": 5.2, "sepalWidth": 4.1, "petalLength": 1.5, "petalWidth": 0.1, "species": "setosa"}, 35 | {"sepalLength": 5.5, "sepalWidth": 4.2, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"}, 36 | {"sepalLength": 4.9, "sepalWidth": 3.1, "petalLength": 1.5, "petalWidth": 0.2, "species": "setosa"}, 37 | {"sepalLength": 5.0, "sepalWidth": 3.2, "petalLength": 1.2, "petalWidth": 0.2, "species": "setosa"}, 38 | {"sepalLength": 5.5, "sepalWidth": 3.5, "petalLength": 1.3, "petalWidth": 0.2, "species": "setosa"}, 39 | {"sepalLength": 4.9, "sepalWidth": 3.6, "petalLength": 1.4, "petalWidth": 0.1, "species": "setosa"}, 40 | {"sepalLength": 4.4, "sepalWidth": 3.0, "petalLength": 1.3, "petalWidth": 0.2, "species": "setosa"}, 41 | {"sepalLength": 5.1, "sepalWidth": 3.4, "petalLength": 1.5, "petalWidth": 0.2, "species": "setosa"}, 42 | {"sepalLength": 5.0, "sepalWidth": 3.5, "petalLength": 1.3, "petalWidth": 0.3, "species": "setosa"}, 43 | {"sepalLength": 4.5, "sepalWidth": 2.3, "petalLength": 1.3, "petalWidth": 0.3, "species": "setosa"}, 44 | {"sepalLength": 4.4, "sepalWidth": 3.2, "petalLength": 1.3, "petalWidth": 0.2, "species": "setosa"}, 45 | {"sepalLength": 5.0, "sepalWidth": 3.5, "petalLength": 1.6, "petalWidth": 0.6, "species": "setosa"}, 46 | {"sepalLength": 5.1, "sepalWidth": 3.8, "petalLength": 1.9, "petalWidth": 0.4, "species": "setosa"}, 47 | {"sepalLength": 4.8, "sepalWidth": 3.0, "petalLength": 1.4, "petalWidth": 0.3, "species": "setosa"}, 48 | {"sepalLength": 5.1, "sepalWidth": 3.8, "petalLength": 1.6, "petalWidth": 0.2, "species": "setosa"}, 49 | {"sepalLength": 4.6, "sepalWidth": 3.2, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"}, 50 | {"sepalLength": 5.3, "sepalWidth": 3.7, "petalLength": 1.5, "petalWidth": 0.2, "species": "setosa"}, 51 | {"sepalLength": 5.0, "sepalWidth": 3.3, "petalLength": 1.4, "petalWidth": 0.2, "species": "setosa"}, 52 | {"sepalLength": 7.0, "sepalWidth": 3.2, "petalLength": 4.7, "petalWidth": 1.4, "species": "versicolor"}, 53 | {"sepalLength": 6.4, "sepalWidth": 3.2, "petalLength": 4.5, "petalWidth": 1.5, "species": "versicolor"}, 54 | {"sepalLength": 6.9, "sepalWidth": 3.1, "petalLength": 4.9, "petalWidth": 1.5, "species": "versicolor"}, 55 | {"sepalLength": 5.5, "sepalWidth": 2.3, "petalLength": 4.0, "petalWidth": 1.3, "species": "versicolor"}, 56 | {"sepalLength": 6.5, "sepalWidth": 2.8, "petalLength": 4.6, "petalWidth": 1.5, "species": "versicolor"}, 57 | {"sepalLength": 5.7, "sepalWidth": 2.8, "petalLength": 4.5, "petalWidth": 1.3, "species": "versicolor"}, 58 | {"sepalLength": 6.3, "sepalWidth": 3.3, "petalLength": 4.7, "petalWidth": 1.6, "species": "versicolor"}, 59 | {"sepalLength": 4.9, "sepalWidth": 2.4, "petalLength": 3.3, "petalWidth": 1.0, "species": "versicolor"}, 60 | {"sepalLength": 6.6, "sepalWidth": 2.9, "petalLength": 4.6, "petalWidth": 1.3, "species": "versicolor"}, 61 | {"sepalLength": 5.2, "sepalWidth": 2.7, "petalLength": 3.9, "petalWidth": 1.4, "species": "versicolor"}, 62 | {"sepalLength": 5.0, "sepalWidth": 2.0, "petalLength": 3.5, "petalWidth": 1.0, "species": "versicolor"}, 63 | {"sepalLength": 5.9, "sepalWidth": 3.0, "petalLength": 4.2, "petalWidth": 1.5, "species": "versicolor"}, 64 | {"sepalLength": 6.0, "sepalWidth": 2.2, "petalLength": 4.0, "petalWidth": 1.0, "species": "versicolor"}, 65 | {"sepalLength": 6.1, "sepalWidth": 2.9, "petalLength": 4.7, "petalWidth": 1.4, "species": "versicolor"}, 66 | {"sepalLength": 5.6, "sepalWidth": 2.9, "petalLength": 3.6, "petalWidth": 1.3, "species": "versicolor"}, 67 | {"sepalLength": 6.7, "sepalWidth": 3.1, "petalLength": 4.4, "petalWidth": 1.4, "species": "versicolor"}, 68 | {"sepalLength": 5.6, "sepalWidth": 3.0, "petalLength": 4.5, "petalWidth": 1.5, "species": "versicolor"}, 69 | {"sepalLength": 5.8, "sepalWidth": 2.7, "petalLength": 4.1, "petalWidth": 1.0, "species": "versicolor"}, 70 | {"sepalLength": 6.2, "sepalWidth": 2.2, "petalLength": 4.5, "petalWidth": 1.5, "species": "versicolor"}, 71 | {"sepalLength": 5.6, "sepalWidth": 2.5, "petalLength": 3.9, "petalWidth": 1.1, "species": "versicolor"}, 72 | {"sepalLength": 5.9, "sepalWidth": 3.2, "petalLength": 4.8, "petalWidth": 1.8, "species": "versicolor"}, 73 | {"sepalLength": 6.1, "sepalWidth": 2.8, "petalLength": 4.0, "petalWidth": 1.3, "species": "versicolor"}, 74 | {"sepalLength": 6.3, "sepalWidth": 2.5, "petalLength": 4.9, "petalWidth": 1.5, "species": "versicolor"}, 75 | {"sepalLength": 6.1, "sepalWidth": 2.8, "petalLength": 4.7, "petalWidth": 1.2, "species": "versicolor"}, 76 | {"sepalLength": 6.4, "sepalWidth": 2.9, "petalLength": 4.3, "petalWidth": 1.3, "species": "versicolor"}, 77 | {"sepalLength": 6.6, "sepalWidth": 3.0, "petalLength": 4.4, "petalWidth": 1.4, "species": "versicolor"}, 78 | {"sepalLength": 6.8, "sepalWidth": 2.8, "petalLength": 4.8, "petalWidth": 1.4, "species": "versicolor"}, 79 | {"sepalLength": 6.7, "sepalWidth": 3.0, "petalLength": 5.0, "petalWidth": 1.7, "species": "versicolor"}, 80 | {"sepalLength": 6.0, "sepalWidth": 2.9, "petalLength": 4.5, "petalWidth": 1.5, "species": "versicolor"}, 81 | {"sepalLength": 5.7, "sepalWidth": 2.6, "petalLength": 3.5, "petalWidth": 1.0, "species": "versicolor"}, 82 | {"sepalLength": 5.5, "sepalWidth": 2.4, "petalLength": 3.8, "petalWidth": 1.1, "species": "versicolor"}, 83 | {"sepalLength": 5.5, "sepalWidth": 2.4, "petalLength": 3.7, "petalWidth": 1.0, "species": "versicolor"}, 84 | {"sepalLength": 5.8, "sepalWidth": 2.7, "petalLength": 3.9, "petalWidth": 1.2, "species": "versicolor"}, 85 | {"sepalLength": 6.0, "sepalWidth": 2.7, "petalLength": 5.1, "petalWidth": 1.6, "species": "versicolor"}, 86 | {"sepalLength": 5.4, "sepalWidth": 3.0, "petalLength": 4.5, "petalWidth": 1.5, "species": "versicolor"}, 87 | {"sepalLength": 6.0, "sepalWidth": 3.4, "petalLength": 4.5, "petalWidth": 1.6, "species": "versicolor"}, 88 | {"sepalLength": 6.7, "sepalWidth": 3.1, "petalLength": 4.7, "petalWidth": 1.5, "species": "versicolor"}, 89 | {"sepalLength": 6.3, "sepalWidth": 2.3, "petalLength": 4.4, "petalWidth": 1.3, "species": "versicolor"}, 90 | {"sepalLength": 5.6, "sepalWidth": 3.0, "petalLength": 4.1, "petalWidth": 1.3, "species": "versicolor"}, 91 | {"sepalLength": 5.5, "sepalWidth": 2.5, "petalLength": 4.0, "petalWidth": 1.3, "species": "versicolor"}, 92 | {"sepalLength": 5.5, "sepalWidth": 2.6, "petalLength": 4.4, "petalWidth": 1.2, "species": "versicolor"}, 93 | {"sepalLength": 6.1, "sepalWidth": 3.0, "petalLength": 4.6, "petalWidth": 1.4, "species": "versicolor"}, 94 | {"sepalLength": 5.8, "sepalWidth": 2.6, "petalLength": 4.0, "petalWidth": 1.2, "species": "versicolor"}, 95 | {"sepalLength": 5.0, "sepalWidth": 2.3, "petalLength": 3.3, "petalWidth": 1.0, "species": "versicolor"}, 96 | {"sepalLength": 5.6, "sepalWidth": 2.7, "petalLength": 4.2, "petalWidth": 1.3, "species": "versicolor"}, 97 | {"sepalLength": 5.7, "sepalWidth": 3.0, "petalLength": 4.2, "petalWidth": 1.2, "species": "versicolor"}, 98 | {"sepalLength": 5.7, "sepalWidth": 2.9, "petalLength": 4.2, "petalWidth": 1.3, "species": "versicolor"}, 99 | {"sepalLength": 6.2, "sepalWidth": 2.9, "petalLength": 4.3, "petalWidth": 1.3, "species": "versicolor"}, 100 | {"sepalLength": 5.1, "sepalWidth": 2.5, "petalLength": 3.0, "petalWidth": 1.1, "species": "versicolor"}, 101 | {"sepalLength": 5.7, "sepalWidth": 2.8, "petalLength": 4.1, "petalWidth": 1.3, "species": "versicolor"}, 102 | {"sepalLength": 6.3, "sepalWidth": 3.3, "petalLength": 6.0, "petalWidth": 2.5, "species": "virginica"}, 103 | {"sepalLength": 5.8, "sepalWidth": 2.7, "petalLength": 5.1, "petalWidth": 1.9, "species": "virginica"}, 104 | {"sepalLength": 7.1, "sepalWidth": 3.0, "petalLength": 5.9, "petalWidth": 2.1, "species": "virginica"}, 105 | {"sepalLength": 6.3, "sepalWidth": 2.9, "petalLength": 5.6, "petalWidth": 1.8, "species": "virginica"}, 106 | {"sepalLength": 6.5, "sepalWidth": 3.0, "petalLength": 5.8, "petalWidth": 2.2, "species": "virginica"}, 107 | {"sepalLength": 7.6, "sepalWidth": 3.0, "petalLength": 6.6, "petalWidth": 2.1, "species": "virginica"}, 108 | {"sepalLength": 4.9, "sepalWidth": 2.5, "petalLength": 4.5, "petalWidth": 1.7, "species": "virginica"}, 109 | {"sepalLength": 7.3, "sepalWidth": 2.9, "petalLength": 6.3, "petalWidth": 1.8, "species": "virginica"}, 110 | {"sepalLength": 6.7, "sepalWidth": 2.5, "petalLength": 5.8, "petalWidth": 1.8, "species": "virginica"}, 111 | {"sepalLength": 7.2, "sepalWidth": 3.6, "petalLength": 6.1, "petalWidth": 2.5, "species": "virginica"}, 112 | {"sepalLength": 6.5, "sepalWidth": 3.2, "petalLength": 5.1, "petalWidth": 2.0, "species": "virginica"}, 113 | {"sepalLength": 6.4, "sepalWidth": 2.7, "petalLength": 5.3, "petalWidth": 1.9, "species": "virginica"}, 114 | {"sepalLength": 6.8, "sepalWidth": 3.0, "petalLength": 5.5, "petalWidth": 2.1, "species": "virginica"}, 115 | {"sepalLength": 5.7, "sepalWidth": 2.5, "petalLength": 5.0, "petalWidth": 2.0, "species": "virginica"}, 116 | {"sepalLength": 5.8, "sepalWidth": 2.8, "petalLength": 5.1, "petalWidth": 2.4, "species": "virginica"}, 117 | {"sepalLength": 6.4, "sepalWidth": 3.2, "petalLength": 5.3, "petalWidth": 2.3, "species": "virginica"}, 118 | {"sepalLength": 6.5, "sepalWidth": 3.0, "petalLength": 5.5, "petalWidth": 1.8, "species": "virginica"}, 119 | {"sepalLength": 7.7, "sepalWidth": 3.8, "petalLength": 6.7, "petalWidth": 2.2, "species": "virginica"}, 120 | {"sepalLength": 7.7, "sepalWidth": 2.6, "petalLength": 6.9, "petalWidth": 2.3, "species": "virginica"}, 121 | {"sepalLength": 6.0, "sepalWidth": 2.2, "petalLength": 5.0, "petalWidth": 1.5, "species": "virginica"}, 122 | {"sepalLength": 6.9, "sepalWidth": 3.2, "petalLength": 5.7, "petalWidth": 2.3, "species": "virginica"}, 123 | {"sepalLength": 5.6, "sepalWidth": 2.8, "petalLength": 4.9, "petalWidth": 2.0, "species": "virginica"}, 124 | {"sepalLength": 7.7, "sepalWidth": 2.8, "petalLength": 6.7, "petalWidth": 2.0, "species": "virginica"}, 125 | {"sepalLength": 6.3, "sepalWidth": 2.7, "petalLength": 4.9, "petalWidth": 1.8, "species": "virginica"}, 126 | {"sepalLength": 6.7, "sepalWidth": 3.3, "petalLength": 5.7, "petalWidth": 2.1, "species": "virginica"}, 127 | {"sepalLength": 7.2, "sepalWidth": 3.2, "petalLength": 6.0, "petalWidth": 1.8, "species": "virginica"}, 128 | {"sepalLength": 6.2, "sepalWidth": 2.8, "petalLength": 4.8, "petalWidth": 1.8, "species": "virginica"}, 129 | {"sepalLength": 6.1, "sepalWidth": 3.0, "petalLength": 4.9, "petalWidth": 1.8, "species": "virginica"}, 130 | {"sepalLength": 6.4, "sepalWidth": 2.8, "petalLength": 5.6, "petalWidth": 2.1, "species": "virginica"}, 131 | {"sepalLength": 7.2, "sepalWidth": 3.0, "petalLength": 5.8, "petalWidth": 1.6, "species": "virginica"}, 132 | {"sepalLength": 7.4, "sepalWidth": 2.8, "petalLength": 6.1, "petalWidth": 1.9, "species": "virginica"}, 133 | {"sepalLength": 7.9, "sepalWidth": 3.8, "petalLength": 6.4, "petalWidth": 2.0, "species": "virginica"}, 134 | {"sepalLength": 6.4, "sepalWidth": 2.8, "petalLength": 5.6, "petalWidth": 2.2, "species": "virginica"}, 135 | {"sepalLength": 6.3, "sepalWidth": 2.8, "petalLength": 5.1, "petalWidth": 1.5, "species": "virginica"}, 136 | {"sepalLength": 6.1, "sepalWidth": 2.6, "petalLength": 5.6, "petalWidth": 1.4, "species": "virginica"}, 137 | {"sepalLength": 7.7, "sepalWidth": 3.0, "petalLength": 6.1, "petalWidth": 2.3, "species": "virginica"}, 138 | {"sepalLength": 6.3, "sepalWidth": 3.4, "petalLength": 5.6, "petalWidth": 2.4, "species": "virginica"}, 139 | {"sepalLength": 6.4, "sepalWidth": 3.1, "petalLength": 5.5, "petalWidth": 1.8, "species": "virginica"}, 140 | {"sepalLength": 6.0, "sepalWidth": 3.0, "petalLength": 4.8, "petalWidth": 1.8, "species": "virginica"}, 141 | {"sepalLength": 6.9, "sepalWidth": 3.1, "petalLength": 5.4, "petalWidth": 2.1, "species": "virginica"}, 142 | {"sepalLength": 6.7, "sepalWidth": 3.1, "petalLength": 5.6, "petalWidth": 2.4, "species": "virginica"}, 143 | {"sepalLength": 6.9, "sepalWidth": 3.1, "petalLength": 5.1, "petalWidth": 2.3, "species": "virginica"}, 144 | {"sepalLength": 5.8, "sepalWidth": 2.7, "petalLength": 5.1, "petalWidth": 1.9, "species": "virginica"}, 145 | {"sepalLength": 6.8, "sepalWidth": 3.2, "petalLength": 5.9, "petalWidth": 2.3, "species": "virginica"}, 146 | {"sepalLength": 6.7, "sepalWidth": 3.3, "petalLength": 5.7, "petalWidth": 2.5, "species": "virginica"}, 147 | {"sepalLength": 6.7, "sepalWidth": 3.0, "petalLength": 5.2, "petalWidth": 2.3, "species": "virginica"}, 148 | {"sepalLength": 6.3, "sepalWidth": 2.5, "petalLength": 5.0, "petalWidth": 1.9, "species": "virginica"}, 149 | {"sepalLength": 6.5, "sepalWidth": 3.0, "petalLength": 5.2, "petalWidth": 2.0, "species": "virginica"}, 150 | {"sepalLength": 6.2, "sepalWidth": 3.4, "petalLength": 5.4, "petalWidth": 2.3, "species": "virginica"}, 151 | {"sepalLength": 5.9, "sepalWidth": 3.0, "petalLength": 5.1, "petalWidth": 1.8, "species": "virginica"} 152 | ] -------------------------------------------------------------------------------- /src/data/population.json: -------------------------------------------------------------------------------- 1 | [{"year":1850,"age":0,"sex":1,"people":1483789}, 2 | {"year":1850,"age":0,"sex":2,"people":1450376}, 3 | {"year":1850,"age":5,"sex":1,"people":1411067}, 4 | {"year":1850,"age":5,"sex":2,"people":1359668}, 5 | {"year":1850,"age":10,"sex":1,"people":1260099}, 6 | {"year":1850,"age":10,"sex":2,"people":1216114}, 7 | {"year":1850,"age":15,"sex":1,"people":1077133}, 8 | {"year":1850,"age":15,"sex":2,"people":1110619}, 9 | {"year":1850,"age":20,"sex":1,"people":1017281}, 10 | {"year":1850,"age":20,"sex":2,"people":1003841}, 11 | {"year":1850,"age":25,"sex":1,"people":862547}, 12 | {"year":1850,"age":25,"sex":2,"people":799482}, 13 | {"year":1850,"age":30,"sex":1,"people":730638}, 14 | {"year":1850,"age":30,"sex":2,"people":639636}, 15 | {"year":1850,"age":35,"sex":1,"people":588487}, 16 | {"year":1850,"age":35,"sex":2,"people":505012}, 17 | {"year":1850,"age":40,"sex":1,"people":475911}, 18 | {"year":1850,"age":40,"sex":2,"people":428185}, 19 | {"year":1850,"age":45,"sex":1,"people":384211}, 20 | {"year":1850,"age":45,"sex":2,"people":341254}, 21 | {"year":1850,"age":50,"sex":1,"people":321343}, 22 | {"year":1850,"age":50,"sex":2,"people":286580}, 23 | {"year":1850,"age":55,"sex":1,"people":194080}, 24 | {"year":1850,"age":55,"sex":2,"people":187208}, 25 | {"year":1850,"age":60,"sex":1,"people":174976}, 26 | {"year":1850,"age":60,"sex":2,"people":162236}, 27 | {"year":1850,"age":65,"sex":1,"people":106827}, 28 | {"year":1850,"age":65,"sex":2,"people":105534}, 29 | {"year":1850,"age":70,"sex":1,"people":73677}, 30 | {"year":1850,"age":70,"sex":2,"people":71762}, 31 | {"year":1850,"age":75,"sex":1,"people":40834}, 32 | {"year":1850,"age":75,"sex":2,"people":40229}, 33 | {"year":1850,"age":80,"sex":1,"people":23449}, 34 | {"year":1850,"age":80,"sex":2,"people":22949}, 35 | {"year":1850,"age":85,"sex":1,"people":8186}, 36 | {"year":1850,"age":85,"sex":2,"people":10511}, 37 | {"year":1850,"age":90,"sex":1,"people":5259}, 38 | {"year":1850,"age":90,"sex":2,"people":6569}, 39 | {"year":1860,"age":0,"sex":1,"people":2120846}, 40 | {"year":1860,"age":0,"sex":2,"people":2092162}, 41 | {"year":1860,"age":5,"sex":1,"people":1804467}, 42 | {"year":1860,"age":5,"sex":2,"people":1778772}, 43 | {"year":1860,"age":10,"sex":1,"people":1612640}, 44 | {"year":1860,"age":10,"sex":2,"people":1540350}, 45 | {"year":1860,"age":15,"sex":1,"people":1438094}, 46 | {"year":1860,"age":15,"sex":2,"people":1495999}, 47 | {"year":1860,"age":20,"sex":1,"people":1351121}, 48 | {"year":1860,"age":20,"sex":2,"people":1370462}, 49 | {"year":1860,"age":25,"sex":1,"people":1217615}, 50 | {"year":1860,"age":25,"sex":2,"people":1116373}, 51 | {"year":1860,"age":30,"sex":1,"people":1043174}, 52 | {"year":1860,"age":30,"sex":2,"people":936055}, 53 | {"year":1860,"age":35,"sex":1,"people":866910}, 54 | {"year":1860,"age":35,"sex":2,"people":737136}, 55 | {"year":1860,"age":40,"sex":1,"people":699434}, 56 | {"year":1860,"age":40,"sex":2,"people":616826}, 57 | {"year":1860,"age":45,"sex":1,"people":552404}, 58 | {"year":1860,"age":45,"sex":2,"people":461739}, 59 | {"year":1860,"age":50,"sex":1,"people":456176}, 60 | {"year":1860,"age":50,"sex":2,"people":407305}, 61 | {"year":1860,"age":55,"sex":1,"people":292417}, 62 | {"year":1860,"age":55,"sex":2,"people":267224}, 63 | {"year":1860,"age":60,"sex":1,"people":260887}, 64 | {"year":1860,"age":60,"sex":2,"people":249735}, 65 | {"year":1860,"age":65,"sex":1,"people":149331}, 66 | {"year":1860,"age":65,"sex":2,"people":141405}, 67 | {"year":1860,"age":70,"sex":1,"people":98465}, 68 | {"year":1860,"age":70,"sex":2,"people":101778}, 69 | {"year":1860,"age":75,"sex":1,"people":56699}, 70 | {"year":1860,"age":75,"sex":2,"people":57597}, 71 | {"year":1860,"age":80,"sex":1,"people":29007}, 72 | {"year":1860,"age":80,"sex":2,"people":29506}, 73 | {"year":1860,"age":85,"sex":1,"people":10434}, 74 | {"year":1860,"age":85,"sex":2,"people":14053}, 75 | {"year":1860,"age":90,"sex":1,"people":7232}, 76 | {"year":1860,"age":90,"sex":2,"people":6622}, 77 | {"year":1870,"age":0,"sex":1,"people":2800083}, 78 | {"year":1870,"age":0,"sex":2,"people":2717102}, 79 | {"year":1870,"age":5,"sex":1,"people":2428469}, 80 | {"year":1870,"age":5,"sex":2,"people":2393680}, 81 | {"year":1870,"age":10,"sex":1,"people":2427341}, 82 | {"year":1870,"age":10,"sex":2,"people":2342670}, 83 | {"year":1870,"age":15,"sex":1,"people":1958390}, 84 | {"year":1870,"age":15,"sex":2,"people":2077248}, 85 | {"year":1870,"age":20,"sex":1,"people":1805303}, 86 | {"year":1870,"age":20,"sex":2,"people":1909382}, 87 | {"year":1870,"age":25,"sex":1,"people":1509059}, 88 | {"year":1870,"age":25,"sex":2,"people":1574285}, 89 | {"year":1870,"age":30,"sex":1,"people":1251534}, 90 | {"year":1870,"age":30,"sex":2,"people":1275629}, 91 | {"year":1870,"age":35,"sex":1,"people":1185336}, 92 | {"year":1870,"age":35,"sex":2,"people":1137490}, 93 | {"year":1870,"age":40,"sex":1,"people":968861}, 94 | {"year":1870,"age":40,"sex":2,"people":944401}, 95 | {"year":1870,"age":45,"sex":1,"people":852672}, 96 | {"year":1870,"age":45,"sex":2,"people":747916}, 97 | {"year":1870,"age":50,"sex":1,"people":736387}, 98 | {"year":1870,"age":50,"sex":2,"people":637801}, 99 | {"year":1870,"age":55,"sex":1,"people":486036}, 100 | {"year":1870,"age":55,"sex":2,"people":407819}, 101 | {"year":1870,"age":60,"sex":1,"people":399264}, 102 | {"year":1870,"age":60,"sex":2,"people":374801}, 103 | {"year":1870,"age":65,"sex":1,"people":260829}, 104 | {"year":1870,"age":65,"sex":2,"people":239080}, 105 | {"year":1870,"age":70,"sex":1,"people":173364}, 106 | {"year":1870,"age":70,"sex":2,"people":165501}, 107 | {"year":1870,"age":75,"sex":1,"people":86929}, 108 | {"year":1870,"age":75,"sex":2,"people":89540}, 109 | {"year":1870,"age":80,"sex":1,"people":47427}, 110 | {"year":1870,"age":80,"sex":2,"people":54190}, 111 | {"year":1870,"age":85,"sex":1,"people":15891}, 112 | {"year":1870,"age":85,"sex":2,"people":19302}, 113 | {"year":1870,"age":90,"sex":1,"people":8649}, 114 | {"year":1870,"age":90,"sex":2,"people":13068}, 115 | {"year":1880,"age":0,"sex":1,"people":3533662}, 116 | {"year":1880,"age":0,"sex":2,"people":3421597}, 117 | {"year":1880,"age":5,"sex":1,"people":3297503}, 118 | {"year":1880,"age":5,"sex":2,"people":3179142}, 119 | {"year":1880,"age":10,"sex":1,"people":2911924}, 120 | {"year":1880,"age":10,"sex":2,"people":2813550}, 121 | {"year":1880,"age":15,"sex":1,"people":2457734}, 122 | {"year":1880,"age":15,"sex":2,"people":2527818}, 123 | {"year":1880,"age":20,"sex":1,"people":2547780}, 124 | {"year":1880,"age":20,"sex":2,"people":2512803}, 125 | {"year":1880,"age":25,"sex":1,"people":2119393}, 126 | {"year":1880,"age":25,"sex":2,"people":1974241}, 127 | {"year":1880,"age":30,"sex":1,"people":1749107}, 128 | {"year":1880,"age":30,"sex":2,"people":1596772}, 129 | {"year":1880,"age":35,"sex":1,"people":1540772}, 130 | {"year":1880,"age":35,"sex":2,"people":1483717}, 131 | {"year":1880,"age":40,"sex":1,"people":1237347}, 132 | {"year":1880,"age":40,"sex":2,"people":1239435}, 133 | {"year":1880,"age":45,"sex":1,"people":1065973}, 134 | {"year":1880,"age":45,"sex":2,"people":1003711}, 135 | {"year":1880,"age":50,"sex":1,"people":964484}, 136 | {"year":1880,"age":50,"sex":2,"people":863012}, 137 | {"year":1880,"age":55,"sex":1,"people":679147}, 138 | {"year":1880,"age":55,"sex":2,"people":594843}, 139 | {"year":1880,"age":60,"sex":1,"people":580298}, 140 | {"year":1880,"age":60,"sex":2,"people":526956}, 141 | {"year":1880,"age":65,"sex":1,"people":369398}, 142 | {"year":1880,"age":65,"sex":2,"people":346303}, 143 | {"year":1880,"age":70,"sex":1,"people":255422}, 144 | {"year":1880,"age":70,"sex":2,"people":251860}, 145 | {"year":1880,"age":75,"sex":1,"people":141628}, 146 | {"year":1880,"age":75,"sex":2,"people":143513}, 147 | {"year":1880,"age":80,"sex":1,"people":67526}, 148 | {"year":1880,"age":80,"sex":2,"people":77290}, 149 | {"year":1880,"age":85,"sex":1,"people":22437}, 150 | {"year":1880,"age":85,"sex":2,"people":31227}, 151 | {"year":1880,"age":90,"sex":1,"people":10272}, 152 | {"year":1880,"age":90,"sex":2,"people":15451}, 153 | {"year":1900,"age":0,"sex":1,"people":4619544}, 154 | {"year":1900,"age":0,"sex":2,"people":4589196}, 155 | {"year":1900,"age":5,"sex":1,"people":4465783}, 156 | {"year":1900,"age":5,"sex":2,"people":4390483}, 157 | {"year":1900,"age":10,"sex":1,"people":4057669}, 158 | {"year":1900,"age":10,"sex":2,"people":4001749}, 159 | {"year":1900,"age":15,"sex":1,"people":3774846}, 160 | {"year":1900,"age":15,"sex":2,"people":3801743}, 161 | {"year":1900,"age":20,"sex":1,"people":3694038}, 162 | {"year":1900,"age":20,"sex":2,"people":3751061}, 163 | {"year":1900,"age":25,"sex":1,"people":3389280}, 164 | {"year":1900,"age":25,"sex":2,"people":3236056}, 165 | {"year":1900,"age":30,"sex":1,"people":2918964}, 166 | {"year":1900,"age":30,"sex":2,"people":2665174}, 167 | {"year":1900,"age":35,"sex":1,"people":2633883}, 168 | {"year":1900,"age":35,"sex":2,"people":2347737}, 169 | {"year":1900,"age":40,"sex":1,"people":2261070}, 170 | {"year":1900,"age":40,"sex":2,"people":2004987}, 171 | {"year":1900,"age":45,"sex":1,"people":1868413}, 172 | {"year":1900,"age":45,"sex":2,"people":1648025}, 173 | {"year":1900,"age":50,"sex":1,"people":1571038}, 174 | {"year":1900,"age":50,"sex":2,"people":1411981}, 175 | {"year":1900,"age":55,"sex":1,"people":1161908}, 176 | {"year":1900,"age":55,"sex":2,"people":1064632}, 177 | {"year":1900,"age":60,"sex":1,"people":916571}, 178 | {"year":1900,"age":60,"sex":2,"people":887508}, 179 | {"year":1900,"age":65,"sex":1,"people":672663}, 180 | {"year":1900,"age":65,"sex":2,"people":640212}, 181 | {"year":1900,"age":70,"sex":1,"people":454747}, 182 | {"year":1900,"age":70,"sex":2,"people":440007}, 183 | {"year":1900,"age":75,"sex":1,"people":268211}, 184 | {"year":1900,"age":75,"sex":2,"people":265879}, 185 | {"year":1900,"age":80,"sex":1,"people":127435}, 186 | {"year":1900,"age":80,"sex":2,"people":132449}, 187 | {"year":1900,"age":85,"sex":1,"people":44008}, 188 | {"year":1900,"age":85,"sex":2,"people":48614}, 189 | {"year":1900,"age":90,"sex":1,"people":15164}, 190 | {"year":1900,"age":90,"sex":2,"people":20093}, 191 | {"year":1910,"age":0,"sex":1,"people":5296823}, 192 | {"year":1910,"age":0,"sex":2,"people":5287477}, 193 | {"year":1910,"age":5,"sex":1,"people":4991803}, 194 | {"year":1910,"age":5,"sex":2,"people":4866139}, 195 | {"year":1910,"age":10,"sex":1,"people":4650747}, 196 | {"year":1910,"age":10,"sex":2,"people":4471887}, 197 | {"year":1910,"age":15,"sex":1,"people":4566154}, 198 | {"year":1910,"age":15,"sex":2,"people":4592269}, 199 | {"year":1910,"age":20,"sex":1,"people":4637632}, 200 | {"year":1910,"age":20,"sex":2,"people":4447683}, 201 | {"year":1910,"age":25,"sex":1,"people":4257755}, 202 | {"year":1910,"age":25,"sex":2,"people":3946153}, 203 | {"year":1910,"age":30,"sex":1,"people":3658125}, 204 | {"year":1910,"age":30,"sex":2,"people":3295220}, 205 | {"year":1910,"age":35,"sex":1,"people":3427518}, 206 | {"year":1910,"age":35,"sex":2,"people":3088990}, 207 | {"year":1910,"age":40,"sex":1,"people":2860229}, 208 | {"year":1910,"age":40,"sex":2,"people":2471267}, 209 | {"year":1910,"age":45,"sex":1,"people":2363801}, 210 | {"year":1910,"age":45,"sex":2,"people":2114930}, 211 | {"year":1910,"age":50,"sex":1,"people":2126516}, 212 | {"year":1910,"age":50,"sex":2,"people":1773592}, 213 | {"year":1910,"age":55,"sex":1,"people":1508358}, 214 | {"year":1910,"age":55,"sex":2,"people":1317651}, 215 | {"year":1910,"age":60,"sex":1,"people":1189421}, 216 | {"year":1910,"age":60,"sex":2,"people":1090697}, 217 | {"year":1910,"age":65,"sex":1,"people":850159}, 218 | {"year":1910,"age":65,"sex":2,"people":813868}, 219 | {"year":1910,"age":70,"sex":1,"people":557936}, 220 | {"year":1910,"age":70,"sex":2,"people":547623}, 221 | {"year":1910,"age":75,"sex":1,"people":322679}, 222 | {"year":1910,"age":75,"sex":2,"people":350900}, 223 | {"year":1910,"age":80,"sex":1,"people":161715}, 224 | {"year":1910,"age":80,"sex":2,"people":174315}, 225 | {"year":1910,"age":85,"sex":1,"people":59699}, 226 | {"year":1910,"age":85,"sex":2,"people":62725}, 227 | {"year":1910,"age":90,"sex":1,"people":23929}, 228 | {"year":1910,"age":90,"sex":2,"people":28965}, 229 | {"year":1920,"age":0,"sex":1,"people":5934792}, 230 | {"year":1920,"age":0,"sex":2,"people":5694244}, 231 | {"year":1920,"age":5,"sex":1,"people":5789008}, 232 | {"year":1920,"age":5,"sex":2,"people":5693960}, 233 | {"year":1920,"age":10,"sex":1,"people":5401156}, 234 | {"year":1920,"age":10,"sex":2,"people":5293057}, 235 | {"year":1920,"age":15,"sex":1,"people":4724365}, 236 | {"year":1920,"age":15,"sex":2,"people":4779936}, 237 | {"year":1920,"age":20,"sex":1,"people":4549411}, 238 | {"year":1920,"age":20,"sex":2,"people":4742632}, 239 | {"year":1920,"age":25,"sex":1,"people":4565066}, 240 | {"year":1920,"age":25,"sex":2,"people":4529382}, 241 | {"year":1920,"age":30,"sex":1,"people":4110771}, 242 | {"year":1920,"age":30,"sex":2,"people":3982426}, 243 | {"year":1920,"age":35,"sex":1,"people":4081543}, 244 | {"year":1920,"age":35,"sex":2,"people":3713810}, 245 | {"year":1920,"age":40,"sex":1,"people":3321923}, 246 | {"year":1920,"age":40,"sex":2,"people":3059757}, 247 | {"year":1920,"age":45,"sex":1,"people":3143891}, 248 | {"year":1920,"age":45,"sex":2,"people":2669089}, 249 | {"year":1920,"age":50,"sex":1,"people":2546035}, 250 | {"year":1920,"age":50,"sex":2,"people":2200491}, 251 | {"year":1920,"age":55,"sex":1,"people":1880975}, 252 | {"year":1920,"age":55,"sex":2,"people":1674672}, 253 | {"year":1920,"age":60,"sex":1,"people":1587549}, 254 | {"year":1920,"age":60,"sex":2,"people":1382877}, 255 | {"year":1920,"age":65,"sex":1,"people":1095956}, 256 | {"year":1920,"age":65,"sex":2,"people":989901}, 257 | {"year":1920,"age":70,"sex":1,"people":714618}, 258 | {"year":1920,"age":70,"sex":2,"people":690097}, 259 | {"year":1920,"age":75,"sex":1,"people":417292}, 260 | {"year":1920,"age":75,"sex":2,"people":439465}, 261 | {"year":1920,"age":80,"sex":1,"people":187000}, 262 | {"year":1920,"age":80,"sex":2,"people":211110}, 263 | {"year":1920,"age":85,"sex":1,"people":75991}, 264 | {"year":1920,"age":85,"sex":2,"people":92829}, 265 | {"year":1920,"age":90,"sex":1,"people":22398}, 266 | {"year":1920,"age":90,"sex":2,"people":32085}, 267 | {"year":1930,"age":0,"sex":1,"people":5875250}, 268 | {"year":1930,"age":0,"sex":2,"people":5662530}, 269 | {"year":1930,"age":5,"sex":1,"people":6542592}, 270 | {"year":1930,"age":5,"sex":2,"people":6129561}, 271 | {"year":1930,"age":10,"sex":1,"people":6064820}, 272 | {"year":1930,"age":10,"sex":2,"people":5986529}, 273 | {"year":1930,"age":15,"sex":1,"people":5709452}, 274 | {"year":1930,"age":15,"sex":2,"people":5769587}, 275 | {"year":1930,"age":20,"sex":1,"people":5305992}, 276 | {"year":1930,"age":20,"sex":2,"people":5565382}, 277 | {"year":1930,"age":25,"sex":1,"people":4929853}, 278 | {"year":1930,"age":25,"sex":2,"people":5050229}, 279 | {"year":1930,"age":30,"sex":1,"people":4424408}, 280 | {"year":1930,"age":30,"sex":2,"people":4455213}, 281 | {"year":1930,"age":35,"sex":1,"people":4576531}, 282 | {"year":1930,"age":35,"sex":2,"people":4593776}, 283 | {"year":1930,"age":40,"sex":1,"people":4075139}, 284 | {"year":1930,"age":40,"sex":2,"people":3754022}, 285 | {"year":1930,"age":45,"sex":1,"people":3633152}, 286 | {"year":1930,"age":45,"sex":2,"people":3396558}, 287 | {"year":1930,"age":50,"sex":1,"people":3128108}, 288 | {"year":1930,"age":50,"sex":2,"people":2809191}, 289 | {"year":1930,"age":55,"sex":1,"people":2434077}, 290 | {"year":1930,"age":55,"sex":2,"people":2298614}, 291 | {"year":1930,"age":60,"sex":1,"people":1927564}, 292 | {"year":1930,"age":60,"sex":2,"people":1783515}, 293 | {"year":1930,"age":65,"sex":1,"people":1397275}, 294 | {"year":1930,"age":65,"sex":2,"people":1307312}, 295 | {"year":1930,"age":70,"sex":1,"people":919045}, 296 | {"year":1930,"age":70,"sex":2,"people":918509}, 297 | {"year":1930,"age":75,"sex":1,"people":536375}, 298 | {"year":1930,"age":75,"sex":2,"people":522716}, 299 | {"year":1930,"age":80,"sex":1,"people":246708}, 300 | {"year":1930,"age":80,"sex":2,"people":283579}, 301 | {"year":1930,"age":85,"sex":1,"people":88978}, 302 | {"year":1930,"age":85,"sex":2,"people":109210}, 303 | {"year":1930,"age":90,"sex":1,"people":30338}, 304 | {"year":1930,"age":90,"sex":2,"people":43483}, 305 | {"year":1940,"age":0,"sex":1,"people":5294628}, 306 | {"year":1940,"age":0,"sex":2,"people":5124653}, 307 | {"year":1940,"age":5,"sex":1,"people":5468378}, 308 | {"year":1940,"age":5,"sex":2,"people":5359099}, 309 | {"year":1940,"age":10,"sex":1,"people":5960416}, 310 | {"year":1940,"age":10,"sex":2,"people":5868532}, 311 | {"year":1940,"age":15,"sex":1,"people":6165109}, 312 | {"year":1940,"age":15,"sex":2,"people":6193701}, 313 | {"year":1940,"age":20,"sex":1,"people":5682414}, 314 | {"year":1940,"age":20,"sex":2,"people":5896002}, 315 | {"year":1940,"age":25,"sex":1,"people":5438166}, 316 | {"year":1940,"age":25,"sex":2,"people":5664244}, 317 | {"year":1940,"age":30,"sex":1,"people":5040048}, 318 | {"year":1940,"age":30,"sex":2,"people":5171522}, 319 | {"year":1940,"age":35,"sex":1,"people":4724804}, 320 | {"year":1940,"age":35,"sex":2,"people":4791809}, 321 | {"year":1940,"age":40,"sex":1,"people":4437392}, 322 | {"year":1940,"age":40,"sex":2,"people":4394061}, 323 | {"year":1940,"age":45,"sex":1,"people":4190187}, 324 | {"year":1940,"age":45,"sex":2,"people":4050290}, 325 | {"year":1940,"age":50,"sex":1,"people":3785735}, 326 | {"year":1940,"age":50,"sex":2,"people":3488396}, 327 | {"year":1940,"age":55,"sex":1,"people":2972069}, 328 | {"year":1940,"age":55,"sex":2,"people":2810000}, 329 | {"year":1940,"age":60,"sex":1,"people":2370232}, 330 | {"year":1940,"age":60,"sex":2,"people":2317790}, 331 | {"year":1940,"age":65,"sex":1,"people":1897678}, 332 | {"year":1940,"age":65,"sex":2,"people":1911117}, 333 | {"year":1940,"age":70,"sex":1,"people":1280023}, 334 | {"year":1940,"age":70,"sex":2,"people":1287711}, 335 | {"year":1940,"age":75,"sex":1,"people":713875}, 336 | {"year":1940,"age":75,"sex":2,"people":764915}, 337 | {"year":1940,"age":80,"sex":1,"people":359418}, 338 | {"year":1940,"age":80,"sex":2,"people":414761}, 339 | {"year":1940,"age":85,"sex":1,"people":127303}, 340 | {"year":1940,"age":85,"sex":2,"people":152131}, 341 | {"year":1940,"age":90,"sex":1,"people":42263}, 342 | {"year":1940,"age":90,"sex":2,"people":58119}, 343 | {"year":1950,"age":0,"sex":1,"people":8211806}, 344 | {"year":1950,"age":0,"sex":2,"people":7862267}, 345 | {"year":1950,"age":5,"sex":1,"people":6706601}, 346 | {"year":1950,"age":5,"sex":2,"people":6450863}, 347 | {"year":1950,"age":10,"sex":1,"people":5629744}, 348 | {"year":1950,"age":10,"sex":2,"people":5430835}, 349 | {"year":1950,"age":15,"sex":1,"people":5264129}, 350 | {"year":1950,"age":15,"sex":2,"people":5288742}, 351 | {"year":1950,"age":20,"sex":1,"people":5573308}, 352 | {"year":1950,"age":20,"sex":2,"people":5854227}, 353 | {"year":1950,"age":25,"sex":1,"people":6007254}, 354 | {"year":1950,"age":25,"sex":2,"people":6317332}, 355 | {"year":1950,"age":30,"sex":1,"people":5676022}, 356 | {"year":1950,"age":30,"sex":2,"people":5895178}, 357 | {"year":1950,"age":35,"sex":1,"people":5511364}, 358 | {"year":1950,"age":35,"sex":2,"people":5696261}, 359 | {"year":1950,"age":40,"sex":1,"people":5076985}, 360 | {"year":1950,"age":40,"sex":2,"people":5199224}, 361 | {"year":1950,"age":45,"sex":1,"people":4533177}, 362 | {"year":1950,"age":45,"sex":2,"people":4595842}, 363 | {"year":1950,"age":50,"sex":1,"people":4199164}, 364 | {"year":1950,"age":50,"sex":2,"people":4147295}, 365 | {"year":1950,"age":55,"sex":1,"people":3667351}, 366 | {"year":1950,"age":55,"sex":2,"people":3595158}, 367 | {"year":1950,"age":60,"sex":1,"people":3035038}, 368 | {"year":1950,"age":60,"sex":2,"people":3009768}, 369 | {"year":1950,"age":65,"sex":1,"people":2421234}, 370 | {"year":1950,"age":65,"sex":2,"people":2548250}, 371 | {"year":1950,"age":70,"sex":1,"people":1627920}, 372 | {"year":1950,"age":70,"sex":2,"people":1786831}, 373 | {"year":1950,"age":75,"sex":1,"people":1006530}, 374 | {"year":1950,"age":75,"sex":2,"people":1148469}, 375 | {"year":1950,"age":80,"sex":1,"people":511727}, 376 | {"year":1950,"age":80,"sex":2,"people":637717}, 377 | {"year":1950,"age":85,"sex":1,"people":182821}, 378 | {"year":1950,"age":85,"sex":2,"people":242798}, 379 | {"year":1950,"age":90,"sex":1,"people":54836}, 380 | {"year":1950,"age":90,"sex":2,"people":90766}, 381 | {"year":1960,"age":0,"sex":1,"people":10374975}, 382 | {"year":1960,"age":0,"sex":2,"people":10146999}, 383 | {"year":1960,"age":5,"sex":1,"people":9495503}, 384 | {"year":1960,"age":5,"sex":2,"people":9250741}, 385 | {"year":1960,"age":10,"sex":1,"people":8563700}, 386 | {"year":1960,"age":10,"sex":2,"people":8310764}, 387 | {"year":1960,"age":15,"sex":1,"people":6620902}, 388 | {"year":1960,"age":15,"sex":2,"people":6617493}, 389 | {"year":1960,"age":20,"sex":1,"people":5268384}, 390 | {"year":1960,"age":20,"sex":2,"people":5513495}, 391 | {"year":1960,"age":25,"sex":1,"people":5311805}, 392 | {"year":1960,"age":25,"sex":2,"people":5548259}, 393 | {"year":1960,"age":30,"sex":1,"people":5801342}, 394 | {"year":1960,"age":30,"sex":2,"people":6090862}, 395 | {"year":1960,"age":35,"sex":1,"people":6063063}, 396 | {"year":1960,"age":35,"sex":2,"people":6431337}, 397 | {"year":1960,"age":40,"sex":1,"people":5657943}, 398 | {"year":1960,"age":40,"sex":2,"people":5940520}, 399 | {"year":1960,"age":45,"sex":1,"people":5345658}, 400 | {"year":1960,"age":45,"sex":2,"people":5516028}, 401 | {"year":1960,"age":50,"sex":1,"people":4763364}, 402 | {"year":1960,"age":50,"sex":2,"people":4928844}, 403 | {"year":1960,"age":55,"sex":1,"people":4170581}, 404 | {"year":1960,"age":55,"sex":2,"people":4402878}, 405 | {"year":1960,"age":60,"sex":1,"people":3405293}, 406 | {"year":1960,"age":60,"sex":2,"people":3723839}, 407 | {"year":1960,"age":65,"sex":1,"people":2859371}, 408 | {"year":1960,"age":65,"sex":2,"people":3268699}, 409 | {"year":1960,"age":70,"sex":1,"people":2115763}, 410 | {"year":1960,"age":70,"sex":2,"people":2516479}, 411 | {"year":1960,"age":75,"sex":1,"people":1308913}, 412 | {"year":1960,"age":75,"sex":2,"people":1641371}, 413 | {"year":1960,"age":80,"sex":1,"people":619923}, 414 | {"year":1960,"age":80,"sex":2,"people":856952}, 415 | {"year":1960,"age":85,"sex":1,"people":253245}, 416 | {"year":1960,"age":85,"sex":2,"people":384572}, 417 | {"year":1960,"age":90,"sex":1,"people":75908}, 418 | {"year":1960,"age":90,"sex":2,"people":135774}, 419 | {"year":1970,"age":0,"sex":1,"people":8685121}, 420 | {"year":1970,"age":0,"sex":2,"people":8326887}, 421 | {"year":1970,"age":5,"sex":1,"people":10411131}, 422 | {"year":1970,"age":5,"sex":2,"people":10003293}, 423 | {"year":1970,"age":10,"sex":1,"people":10756403}, 424 | {"year":1970,"age":10,"sex":2,"people":10343538}, 425 | {"year":1970,"age":15,"sex":1,"people":9605399}, 426 | {"year":1970,"age":15,"sex":2,"people":9414284}, 427 | {"year":1970,"age":20,"sex":1,"people":7729202}, 428 | {"year":1970,"age":20,"sex":2,"people":8341830}, 429 | {"year":1970,"age":25,"sex":1,"people":6539301}, 430 | {"year":1970,"age":25,"sex":2,"people":6903041}, 431 | {"year":1970,"age":30,"sex":1,"people":5519879}, 432 | {"year":1970,"age":30,"sex":2,"people":5851441}, 433 | {"year":1970,"age":35,"sex":1,"people":5396732}, 434 | {"year":1970,"age":35,"sex":2,"people":5708021}, 435 | {"year":1970,"age":40,"sex":1,"people":5718538}, 436 | {"year":1970,"age":40,"sex":2,"people":6129319}, 437 | {"year":1970,"age":45,"sex":1,"people":5794120}, 438 | {"year":1970,"age":45,"sex":2,"people":6198742}, 439 | {"year":1970,"age":50,"sex":1,"people":5298312}, 440 | {"year":1970,"age":50,"sex":2,"people":5783817}, 441 | {"year":1970,"age":55,"sex":1,"people":4762911}, 442 | {"year":1970,"age":55,"sex":2,"people":5222164}, 443 | {"year":1970,"age":60,"sex":1,"people":4037643}, 444 | {"year":1970,"age":60,"sex":2,"people":4577251}, 445 | {"year":1970,"age":65,"sex":1,"people":3142606}, 446 | {"year":1970,"age":65,"sex":2,"people":3894827}, 447 | {"year":1970,"age":70,"sex":1,"people":2340826}, 448 | {"year":1970,"age":70,"sex":2,"people":3138009}, 449 | {"year":1970,"age":75,"sex":1,"people":1599269}, 450 | {"year":1970,"age":75,"sex":2,"people":2293376}, 451 | {"year":1970,"age":80,"sex":1,"people":886155}, 452 | {"year":1970,"age":80,"sex":2,"people":1417553}, 453 | {"year":1970,"age":85,"sex":1,"people":371123}, 454 | {"year":1970,"age":85,"sex":2,"people":658511}, 455 | {"year":1970,"age":90,"sex":1,"people":186502}, 456 | {"year":1970,"age":90,"sex":2,"people":314929}, 457 | {"year":1980,"age":0,"sex":1,"people":8439366}, 458 | {"year":1980,"age":0,"sex":2,"people":8081854}, 459 | {"year":1980,"age":5,"sex":1,"people":8680730}, 460 | {"year":1980,"age":5,"sex":2,"people":8275881}, 461 | {"year":1980,"age":10,"sex":1,"people":9452338}, 462 | {"year":1980,"age":10,"sex":2,"people":9048483}, 463 | {"year":1980,"age":15,"sex":1,"people":10698856}, 464 | {"year":1980,"age":15,"sex":2,"people":10410271}, 465 | {"year":1980,"age":20,"sex":1,"people":10486776}, 466 | {"year":1980,"age":20,"sex":2,"people":10614947}, 467 | {"year":1980,"age":25,"sex":1,"people":9624053}, 468 | {"year":1980,"age":25,"sex":2,"people":9827903}, 469 | {"year":1980,"age":30,"sex":1,"people":8705835}, 470 | {"year":1980,"age":30,"sex":2,"people":8955225}, 471 | {"year":1980,"age":35,"sex":1,"people":6852069}, 472 | {"year":1980,"age":35,"sex":2,"people":7134239}, 473 | {"year":1980,"age":40,"sex":1,"people":5692148}, 474 | {"year":1980,"age":40,"sex":2,"people":5953910}, 475 | {"year":1980,"age":45,"sex":1,"people":5342469}, 476 | {"year":1980,"age":45,"sex":2,"people":5697543}, 477 | {"year":1980,"age":50,"sex":1,"people":5603709}, 478 | {"year":1980,"age":50,"sex":2,"people":6110117}, 479 | {"year":1980,"age":55,"sex":1,"people":5485098}, 480 | {"year":1980,"age":55,"sex":2,"people":6160229}, 481 | {"year":1980,"age":60,"sex":1,"people":4696140}, 482 | {"year":1980,"age":60,"sex":2,"people":5456885}, 483 | {"year":1980,"age":65,"sex":1,"people":3893510}, 484 | {"year":1980,"age":65,"sex":2,"people":4896947}, 485 | {"year":1980,"age":70,"sex":1,"people":2857774}, 486 | {"year":1980,"age":70,"sex":2,"people":3963441}, 487 | {"year":1980,"age":75,"sex":1,"people":1840438}, 488 | {"year":1980,"age":75,"sex":2,"people":2951759}, 489 | {"year":1980,"age":80,"sex":1,"people":1012886}, 490 | {"year":1980,"age":80,"sex":2,"people":1919292}, 491 | {"year":1980,"age":85,"sex":1,"people":472338}, 492 | {"year":1980,"age":85,"sex":2,"people":1023115}, 493 | {"year":1980,"age":90,"sex":1,"people":204148}, 494 | {"year":1980,"age":90,"sex":2,"people":499046}, 495 | {"year":1990,"age":0,"sex":1,"people":9307465}, 496 | {"year":1990,"age":0,"sex":2,"people":8894007}, 497 | {"year":1990,"age":5,"sex":1,"people":9274732}, 498 | {"year":1990,"age":5,"sex":2,"people":8799955}, 499 | {"year":1990,"age":10,"sex":1,"people":8782542}, 500 | {"year":1990,"age":10,"sex":2,"people":8337284}, 501 | {"year":1990,"age":15,"sex":1,"people":9020572}, 502 | {"year":1990,"age":15,"sex":2,"people":8590991}, 503 | {"year":1990,"age":20,"sex":1,"people":9436188}, 504 | {"year":1990,"age":20,"sex":2,"people":9152644}, 505 | {"year":1990,"age":25,"sex":1,"people":10658027}, 506 | {"year":1990,"age":25,"sex":2,"people":10587292}, 507 | {"year":1990,"age":30,"sex":1,"people":11028712}, 508 | {"year":1990,"age":30,"sex":2,"people":11105750}, 509 | {"year":1990,"age":35,"sex":1,"people":9853933}, 510 | {"year":1990,"age":35,"sex":2,"people":10038644}, 511 | {"year":1990,"age":40,"sex":1,"people":8712632}, 512 | {"year":1990,"age":40,"sex":2,"people":8928252}, 513 | {"year":1990,"age":45,"sex":1,"people":6848082}, 514 | {"year":1990,"age":45,"sex":2,"people":7115129}, 515 | {"year":1990,"age":50,"sex":1,"people":5553992}, 516 | {"year":1990,"age":50,"sex":2,"people":5899925}, 517 | {"year":1990,"age":55,"sex":1,"people":4981670}, 518 | {"year":1990,"age":55,"sex":2,"people":5460506}, 519 | {"year":1990,"age":60,"sex":1,"people":4953822}, 520 | {"year":1990,"age":60,"sex":2,"people":5663205}, 521 | {"year":1990,"age":65,"sex":1,"people":4538398}, 522 | {"year":1990,"age":65,"sex":2,"people":5594108}, 523 | {"year":1990,"age":70,"sex":1,"people":3429420}, 524 | {"year":1990,"age":70,"sex":2,"people":4610222}, 525 | {"year":1990,"age":75,"sex":1,"people":2344932}, 526 | {"year":1990,"age":75,"sex":2,"people":3723980}, 527 | {"year":1990,"age":80,"sex":1,"people":1342996}, 528 | {"year":1990,"age":80,"sex":2,"people":2545730}, 529 | {"year":1990,"age":85,"sex":1,"people":588790}, 530 | {"year":1990,"age":85,"sex":2,"people":1419494}, 531 | {"year":1990,"age":90,"sex":1,"people":238459}, 532 | {"year":1990,"age":90,"sex":2,"people":745146}, 533 | {"year":2000,"age":0,"sex":1,"people":9735380}, 534 | {"year":2000,"age":0,"sex":2,"people":9310714}, 535 | {"year":2000,"age":5,"sex":1,"people":10552146}, 536 | {"year":2000,"age":5,"sex":2,"people":10069564}, 537 | {"year":2000,"age":10,"sex":1,"people":10563233}, 538 | {"year":2000,"age":10,"sex":2,"people":10022524}, 539 | {"year":2000,"age":15,"sex":1,"people":10237419}, 540 | {"year":2000,"age":15,"sex":2,"people":9692669}, 541 | {"year":2000,"age":20,"sex":1,"people":9731315}, 542 | {"year":2000,"age":20,"sex":2,"people":9324244}, 543 | {"year":2000,"age":25,"sex":1,"people":9659493}, 544 | {"year":2000,"age":25,"sex":2,"people":9518507}, 545 | {"year":2000,"age":30,"sex":1,"people":10205879}, 546 | {"year":2000,"age":30,"sex":2,"people":10119296}, 547 | {"year":2000,"age":35,"sex":1,"people":11475182}, 548 | {"year":2000,"age":35,"sex":2,"people":11635647}, 549 | {"year":2000,"age":40,"sex":1,"people":11320252}, 550 | {"year":2000,"age":40,"sex":2,"people":11488578}, 551 | {"year":2000,"age":45,"sex":1,"people":9925006}, 552 | {"year":2000,"age":45,"sex":2,"people":10261253}, 553 | {"year":2000,"age":50,"sex":1,"people":8507934}, 554 | {"year":2000,"age":50,"sex":2,"people":8911133}, 555 | {"year":2000,"age":55,"sex":1,"people":6459082}, 556 | {"year":2000,"age":55,"sex":2,"people":6921268}, 557 | {"year":2000,"age":60,"sex":1,"people":5123399}, 558 | {"year":2000,"age":60,"sex":2,"people":5668961}, 559 | {"year":2000,"age":65,"sex":1,"people":4453623}, 560 | {"year":2000,"age":65,"sex":2,"people":4804784}, 561 | {"year":2000,"age":70,"sex":1,"people":3792145}, 562 | {"year":2000,"age":70,"sex":2,"people":5184855}, 563 | {"year":2000,"age":75,"sex":1,"people":2912655}, 564 | {"year":2000,"age":75,"sex":2,"people":4355644}, 565 | {"year":2000,"age":80,"sex":1,"people":1902638}, 566 | {"year":2000,"age":80,"sex":2,"people":3221898}, 567 | {"year":2000,"age":85,"sex":1,"people":970357}, 568 | {"year":2000,"age":85,"sex":2,"people":1981156}, 569 | {"year":2000,"age":90,"sex":1,"people":336303}, 570 | {"year":2000,"age":90,"sex":2,"people":1064581}] --------------------------------------------------------------------------------