├── Rakefile ├── vendor ├── assets │ ├── javascripts │ │ ├── d3_rails.js │ │ ├── chart │ │ │ ├── chart.js │ │ │ ├── horizon.js │ │ │ ├── bullet.js │ │ │ ├── qq.js │ │ │ └── box.js │ │ ├── d3_chart_module.js │ │ ├── .DS_Store │ │ ├── science.lin.js │ │ ├── LICENSE │ │ ├── d3_csv.js │ │ ├── science.js │ │ ├── science.stats.js │ │ ├── d3.time.js │ │ ├── colorbrewer.js │ │ ├── d3.geom.js │ │ ├── d3.geo.js │ │ └── d3.chart.js │ └── stylesheets │ │ ├── bubble.css │ │ ├── d3_rails.css │ │ ├── kde.css │ │ ├── horizon.css │ │ ├── bundle-radial.css │ │ ├── chord.css │ │ ├── force.css │ │ ├── box.css │ │ ├── cluster.css │ │ ├── choropleth.css │ │ ├── bundle-treemap.css │ │ ├── calendar.css │ │ ├── cartogram.css │ │ ├── line.css │ │ ├── azimuthal.css │ │ ├── clock.css │ │ ├── bullet.css │ │ ├── button.css │ │ └── LICENSE └── .DS_Store ├── .gitignore ├── .DS_Store ├── lib ├── d3_rails │ ├── version.rb │ └── engine.rb └── d3_rails.rb ├── Gemfile ├── d3_rails.gemspec ├── LICENSE └── README.md /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/d3_rails.js: -------------------------------------------------------------------------------- 1 | //= require self -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | pkg/* 5 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/chart/chart.js: -------------------------------------------------------------------------------- 1 | d3.chart = {}; 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sandbox/d3_rails/master/.DS_Store -------------------------------------------------------------------------------- /vendor/assets/javascripts/d3_chart_module.js: -------------------------------------------------------------------------------- 1 | //= require_directory ./chart -------------------------------------------------------------------------------- /lib/d3_rails/version.rb: -------------------------------------------------------------------------------- 1 | module D3Rails 2 | VERSION = "2.7.5" 3 | end 4 | -------------------------------------------------------------------------------- /vendor/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sandbox/d3_rails/master/vendor/.DS_Store -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Specify your gem's dependencies in d3_rails.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /lib/d3_rails/engine.rb: -------------------------------------------------------------------------------- 1 | module D3Rails 2 | module Rails 3 | class Engine < ::Rails::Engine 4 | end 5 | end 6 | end -------------------------------------------------------------------------------- /vendor/assets/javascripts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sandbox/d3_rails/master/vendor/assets/javascripts/.DS_Store -------------------------------------------------------------------------------- /lib/d3_rails.rb: -------------------------------------------------------------------------------- 1 | if defined? Rails && Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR >= 1 2 | require 'd3_rails/engine' 3 | end -------------------------------------------------------------------------------- /vendor/assets/stylesheets/bubble.css: -------------------------------------------------------------------------------- 1 | circle { 2 | stroke: #fff; 3 | stroke-width: 1.5px; 4 | } 5 | 6 | text { 7 | font: 10px sans-serif; 8 | } 9 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/d3_rails.css: -------------------------------------------------------------------------------- 1 | /* 2 | *= require chord 3 | *= require choropleth 4 | *= require colorbrewer 5 | *= require force 6 | */ 7 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/kde.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 10px sans-serif; 3 | } 4 | 5 | path { 6 | stroke: #000; 7 | stroke-width: 1.5px; 8 | fill: none; 9 | } 10 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/horizon.css: -------------------------------------------------------------------------------- 1 | #controls { 2 | position: relative; 3 | width: 960px; 4 | } 5 | 6 | #bands { 7 | position: absolute; 8 | right: 0; 9 | } 10 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/bundle-radial.css: -------------------------------------------------------------------------------- 1 | .node { 2 | font: 10px sans-serif; 3 | } 4 | 5 | .link { 6 | stroke: steelblue; 7 | stroke-opacity: .4; 8 | fill: none; 9 | } 10 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/chord.css: -------------------------------------------------------------------------------- 1 | #chart { 2 | font: 10px sans-serif; 3 | } 4 | 5 | .chord path { 6 | fill-opacity: .67; 7 | stroke: #000; 8 | stroke-width: .5px; 9 | } 10 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/force.css: -------------------------------------------------------------------------------- 1 | circle.node { 2 | stroke: #fff; 3 | stroke-width: 1.5px; 4 | } 5 | 6 | line.link { 7 | stroke: #999; 8 | stroke-opacity: .6; 9 | } 10 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/box.css: -------------------------------------------------------------------------------- 1 | .box { font: 10px sans-serif; } 2 | .box line, .box rect, .box circle { stroke: #000; stroke-width: 1.5px; fill: #fff; } 3 | .box .center { stroke-dasharray: 3 3; } 4 | .box .outlier { stroke: #ccc; fill: none; } 5 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/cluster.css: -------------------------------------------------------------------------------- 1 | .node circle { 2 | fill: #fff; 3 | stroke: steelblue; 4 | stroke-width: 1.5px; 5 | } 6 | 7 | .node { 8 | font: 10px sans-serif; 9 | } 10 | 11 | .link { 12 | fill: none; 13 | stroke: #ccc; 14 | stroke-width: 1.5px; 15 | } 16 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/choropleth.css: -------------------------------------------------------------------------------- 1 | svg { 2 | background: #eee; 3 | width: 960px; 4 | height: 500px; 5 | } 6 | 7 | #counties path { 8 | stroke: #fff; 9 | stroke-width: .25px; 10 | } 11 | 12 | #states path { 13 | fill: none; 14 | stroke: #fff; 15 | stroke-width: 1.5px; 16 | } 17 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/bundle-treemap.css: -------------------------------------------------------------------------------- 1 | .cell { 2 | border: solid 1px white; 3 | font: 10px sans-serif; 4 | line-height: 12px; 5 | overflow: hidden; 6 | position: absolute; 7 | text-indent: 2px; 8 | } 9 | 10 | .link { 11 | stroke: #000; 12 | stroke-opacity: .5; 13 | fill: none; 14 | } 15 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/calendar.css: -------------------------------------------------------------------------------- 1 | #chart { 2 | font: 10px sans-serif; 3 | } 4 | 5 | .day { 6 | fill: #fff; 7 | stroke: #ccc; 8 | shape-rendering: crispEdges; 9 | } 10 | 11 | .month { 12 | fill: none; 13 | stroke: #000; 14 | stroke-width: 2px; 15 | shape-rendering: crispEdges; 16 | } 17 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/cartogram.css: -------------------------------------------------------------------------------- 1 | #chart { 2 | width: 960px; 3 | height: 500px; 4 | } 5 | 6 | .black path { 7 | fill: none; 8 | stroke: #ccc; 9 | stroke-width: 3px; 10 | } 11 | 12 | .white path { 13 | fill: #fff; 14 | stroke: #fff; 15 | } 16 | 17 | .grey path { 18 | fill: #ccc; 19 | stroke: #666; 20 | } 21 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/line.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 10px sans-serif; 3 | } 4 | 5 | .rule line { 6 | stroke: #eee; 7 | shape-rendering: crispEdges; 8 | } 9 | 10 | .rule line.axis { 11 | stroke: #000; 12 | } 13 | 14 | .line { 15 | fill: none; 16 | stroke: steelblue; 17 | stroke-width: 1.5px; 18 | } 19 | 20 | circle.line { 21 | fill: #fff; 22 | } 23 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/azimuthal.css: -------------------------------------------------------------------------------- 1 | @import url("../../lib/jquery-ui/jquery-ui.css"); 2 | 3 | body, .ui-widget { 4 | font: 14px Helvetica Neue; 5 | } 6 | 7 | svg { 8 | width: 960px; 9 | height: 500px; 10 | border: solid 1px #ccc; 11 | background: #eee; 12 | } 13 | 14 | path { 15 | fill: #ccc; 16 | stroke: #fff; 17 | } 18 | 19 | div { 20 | width: 960px; 21 | } 22 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/clock.css: -------------------------------------------------------------------------------- 1 | #clock { 2 | position: relative; 3 | background: #222; 4 | width: 960px; 5 | height: 700px; 6 | } 7 | 8 | #clock div { 9 | position: absolute; 10 | right: 4px; 11 | bottom: 4px; 12 | color: #ddd; 13 | font: 10px sans-serif; 14 | } 15 | 16 | #clock a { 17 | color: #fff; 18 | font-weight: bold; 19 | } 20 | 21 | text { 22 | font: bold 13px sans-serif; 23 | } 24 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/bullet.css: -------------------------------------------------------------------------------- 1 | .bullet { font: 10px sans-serif; } 2 | .bullet .marker { stroke: #000; stroke-width: 2px; } 3 | .bullet .tick line { stroke: #666; stroke-width: .5px; } 4 | .bullet .range.s0 { fill: #eee; } 5 | .bullet .range.s1 { fill: #ddd; } 6 | .bullet .range.s2 { fill: #ccc; } 7 | .bullet .measure.s0 { fill: lightsteelblue; } 8 | .bullet .measure.s1 { fill: steelblue; } 9 | .bullet .title { font-size: 14px; font-weight: bold; } 10 | .bullet .subtitle { fill: #999; } 11 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/science.lin.js: -------------------------------------------------------------------------------- 1 | (function(){science.lin = {}; 2 | /** 3 | * Solves tridiagonal systems of linear equations. 4 | * 5 | * Source: http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm 6 | * 7 | * @param {number[]} a 8 | * @param {number[]} b 9 | * @param {number[]} c 10 | * @param {number[]} d 11 | * @param {number[]} x 12 | * @param {number} n 13 | */ 14 | science.lin.tridag = function(a, b, c, d, x, n) { 15 | var i, 16 | m; 17 | for (i = 1; i < n; i++) { 18 | m = a[i] / b[i - 1]; 19 | b[i] -= m * c[i - 1]; 20 | d[i] -= m * d[i - 1]; 21 | } 22 | x[n - 1] = d[n - 1] / b[n - 1]; 23 | for (i = n - 2; i >= 0; i--) { 24 | x[i] = (d[i] - c[i] * x[i + 1]) / b[i]; 25 | } 26 | }; 27 | })() -------------------------------------------------------------------------------- /d3_rails.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "d3_rails/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "d3_rails" 7 | s.version = D3Rails::VERSION 8 | s.authors = ["Han"] 9 | s.email = ["han@logicallsat.com"] 10 | s.homepage = "" 11 | s.summary = %q{D3 automated install for Rails 3.1+} 12 | s.description = %q{Gem installation of javascript framework for data visualization, D3} 13 | 14 | s.rubyforge_project = "d3_rails" 15 | 16 | s.files = `git ls-files`.split("\n") 17 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 18 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 19 | s.require_paths = ["lib", "vendor"] 20 | 21 | s.add_dependency "railties", ">= 3.1.0" 22 | s.add_development_dependency "bundler", "~> 1.0.0" 23 | s.add_development_dependency "rails", "~> 3.1" 24 | end 25 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/button.css: -------------------------------------------------------------------------------- 1 | button { 2 | font: 14px Helvetica Neue; 3 | background-color: #222; 4 | background-image: -moz-linear-gradient(top, rgba(255,255,255,.25), rgba(255,255,255,.11)); 5 | background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, rgba(255,255,255,.25)),color-stop(1, rgba(255,255,255,.11))); 6 | background-image: -webkit-linear-gradient(rgba(255,255,255,.25), rgba(255,255,255,.11)); 7 | color: #fff; 8 | text-rendering: optimizeLegibility; 9 | text-shadow: 0 -1px 1px #222; 10 | padding: 6px 10px 6px 10px; 11 | border: 0; 12 | border-radius: 0; 13 | border-bottom: 1px solid #222; 14 | margin: 0; 15 | -moz-box-shadow: 0 1px 3px #999; 16 | -webkit-box-shadow: 0 1px 3px #999; 17 | } 18 | 19 | button.first { 20 | border-top-left-radius: 5px; 21 | border-bottom-left-radius: 5px; 22 | } 23 | 24 | button.last { 25 | border-top-right-radius: 5px; 26 | border-bottom-right-radius: 5px; 27 | } 28 | 29 | button.active { 30 | background-color: rgb(65,102,133); 31 | } 32 | 33 | button:hover { 34 | background-color: steelblue; 35 | } 36 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Michael Bostock 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 | * The name Michael Bostock may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | D3 is a javascript framework created by Michael Bostock 2 | ------------------------------------------------------------------------ 3 | Copyright (c) 2010, Michael Bostock 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * The name Michael Bostock may not be used to endorse or promote products 17 | derived from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 26 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 28 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | ----------------------------------------------------------------------------- -------------------------------------------------------------------------------- /vendor/assets/stylesheets/LICENSE: -------------------------------------------------------------------------------- 1 | Apache-Style Software License for ColorBrewer software and ColorBrewer Color 2 | Schemes 3 | 4 | Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State 5 | University. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | use this file except in compliance with the License. You may obtain a copy of 9 | the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | License for the specific language governing permissions and limitations under 17 | the License. 18 | 19 | Redistribution and use in source and binary forms, with or without 20 | modification, are permitted provided that the following conditions are met: 21 | 22 | 1. Redistributions as source code must retain the above copyright notice, this 23 | list of conditions and the following disclaimer. 24 | 25 | 2. The end-user documentation included with the redistribution, if any, must 26 | include the following acknowledgment: "This product includes color 27 | specifications and designs developed by Cynthia Brewer 28 | (http://colorbrewer.org/)." Alternately, this acknowledgment may appear in the 29 | software itself, if and wherever such third-party acknowledgments normally 30 | appear. 31 | 32 | 4. The name "ColorBrewer" must not be used to endorse or promote products 33 | derived from this software without prior written permission. For written 34 | permission, please contact Cynthia Brewer at cbrewer@psu.edu. 35 | 36 | 5. Products derived from this software may not be called "ColorBrewer", nor 37 | may "ColorBrewer" appear in their name, without prior written permission of 38 | Cynthia Brewer. 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # D3_Rails 2 | 3 | **D3_Rails** is a small, lightweight gem which allows you to include any 4 | and all of D3 Javascripts into your 3.1 Rails application, merely by 5 | including any desired D3 files into your application.js manifest. 6 | 7 | # About D3 8 | 9 | **D3** is a small, free JavaScript library for manipulating HTML documents 10 | based on data. D3 can help you quickly visualize your data as HTML or SVG, 11 | handle interactivity, and incorporate smooth transitions and staged animations 12 | into your pages. You can use D3 as a visualization framework (like Protovis), 13 | or you can use it to build dynamic pages (like jQuery). 14 | 15 | # D3 Version 16 | 17 | The current release of this gem is using **D3 v=2.7.5** 18 | **Last Updated 2-21-2012** 19 | 20 | # Included Javascripts 21 | **minified js files are not included, since the rails asset pipeline sorta necessitates the compiling (and likely minifying) your js files for production** 22 | 23 | colorbrewer.js 24 | d3_csv.js 25 | d3.js 26 | d3.chart.js 27 | d3.geo.js 28 | d3.geom.js 29 | d3.layout.js 30 | d3.time.js 31 | science.js 32 | science.lin.js 33 | science.stats.js 34 | 35 | # D3 charting module 36 | 37 | ** D3 charts can be be included in your rails application by including the following line in your javascripts manifest ** 38 | 39 | //= require d3_chart_module 40 | 41 | This will include the following javascripts to your application: 42 | 43 | box.js 44 | bullet.js 45 | chart.js 46 | horizon.js 47 | qq.js 48 | 49 | ### Installation 50 | 51 | This gem should work out of the box. All you have to do is add the gem to your Gemfile: 52 | 53 | gem 'd3_rails' 54 | 55 | Then bundle install or update (depending on if you want to upgrade an older version of this gem). 56 | 57 | bundle install 58 | bundle update 59 | 60 | In your assets/javascripts/application.js, you will need to add d3 to your manifest: 61 | 62 | //= require jquery 63 | . 64 | . 65 | //= require d3 66 | 67 | ### Additional Files 68 | 69 | Bundle installing d3_rails will add the main d3.js file to your application. However, if you require the additional javascripts included by the D3, such as d3.layout.js or d3.chart.js, all you need to do is include the desired javascripts in your javascript manifest in your assets/javascripts/application.js file. 70 | 71 | Ex. 72 | 73 | //= require jquery 74 | . 75 | . 76 | //= require d3 77 | //= require d3.chart 78 | etc.. 79 | 80 | ### Versioning 81 | 82 | Version numbers of this gem have been changed as of **2-2-2012** to reflect the d3 version numbers. 83 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/d3_csv.js: -------------------------------------------------------------------------------- 1 | (function(){d3.csv = function(url, callback) { 2 | d3.text(url, "text/csv", function(text) { 3 | callback(text && d3.csv.parse(text)); 4 | }); 5 | }; 6 | d3.csv.parse = function(text) { 7 | var header; 8 | return d3.csv.parseRows(text, function(row, i) { 9 | if (i) { 10 | var o = {}, j = -1, m = header.length; 11 | while (++j < m) o[header[j]] = row[j]; 12 | return o; 13 | } else { 14 | header = row; 15 | return null; 16 | } 17 | }); 18 | }; 19 | 20 | d3.csv.parseRows = function(text, f) { 21 | var EOL = {}, // sentinel value for end-of-line 22 | EOF = {}, // sentinel value for end-of-file 23 | rows = [], // output rows 24 | re = /\r\n|[,\r\n]/g, // field separator regex 25 | n = 0, // the current line number 26 | t, // the current token 27 | eol; // is the current token followed by EOL? 28 | 29 | re.lastIndex = 0; // work-around bug in FF 3.6 30 | 31 | /** @private Returns the next token. */ 32 | function token() { 33 | if (re.lastIndex >= text.length) return EOF; // special case: end of file 34 | if (eol) { eol = false; return EOL; } // special case: end of line 35 | 36 | // special case: quotes 37 | var j = re.lastIndex; 38 | if (text.charCodeAt(j) === 34) { 39 | var i = j; 40 | while (i++ < text.length) { 41 | if (text.charCodeAt(i) === 34) { 42 | if (text.charCodeAt(i + 1) !== 34) break; 43 | i++; 44 | } 45 | } 46 | re.lastIndex = i + 2; 47 | var c = text.charCodeAt(i + 1); 48 | if (c === 13) { 49 | eol = true; 50 | if (text.charCodeAt(i + 2) === 10) re.lastIndex++; 51 | } else if (c === 10) { 52 | eol = true; 53 | } 54 | return text.substring(j + 1, i).replace(/""/g, "\""); 55 | } 56 | 57 | // common case 58 | var m = re.exec(text); 59 | if (m) { 60 | eol = m[0].charCodeAt(0) !== 44; 61 | return text.substring(j, m.index); 62 | } 63 | re.lastIndex = text.length; 64 | return text.substring(j); 65 | } 66 | 67 | while ((t = token()) !== EOF) { 68 | var a = []; 69 | while ((t !== EOL) && (t !== EOF)) { 70 | a.push(t); 71 | t = token(); 72 | } 73 | if (f && !(a = f(a, n++))) continue; 74 | rows.push(a); 75 | } 76 | 77 | return rows; 78 | }; 79 | d3.csv.format = function(rows) { 80 | return rows.map(d3_csv_formatRow).join("\n"); 81 | }; 82 | 83 | function d3_csv_formatRow(row) { 84 | return row.map(d3_csv_formatValue).join(","); 85 | } 86 | 87 | function d3_csv_formatValue(text) { 88 | return /[",\n]/.test(text) 89 | ? "\"" + text.replace(/\"/g, "\"\"") + "\"" 90 | : text; 91 | } 92 | })(); 93 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/science.js: -------------------------------------------------------------------------------- 1 | (function(){science = {version: "1.7.0"}; // semver 2 | science.ascending = function(a, b) { 3 | return a - b; 4 | }; 5 | // Euler's constant. 6 | science.EULER = .5772156649015329; 7 | // Compute exp(x) - 1 accurately for small x. 8 | science.expm1 = function(x) { 9 | return (x < 1e-5 && x > -1e-5) ? x + .5 * x * x : Math.exp(x) - 1; 10 | }; 11 | science.functor = function(v) { 12 | return typeof v === "function" ? v : function() { return v; }; 13 | }; 14 | // Based on: 15 | // http://www.johndcook.com/blog/2010/06/02/whats-so-hard-about-finding-a-hypotenuse/ 16 | science.hypot = function(x, y) { 17 | x = Math.abs(x); 18 | y = Math.abs(y); 19 | var max, 20 | min; 21 | if (x > y) { max = x; min = y; } 22 | else { max = y; min = x; } 23 | var r = min / max; 24 | return max * Math.sqrt(1 + r * r); 25 | }; 26 | science.quadratic = function() { 27 | var complex = false; 28 | 29 | function quadratic(a, b, c) { 30 | var d = b * b - 4 * a * c; 31 | if (d > 0) { 32 | d = Math.sqrt(d) / (2 * a); 33 | return complex 34 | ? [{r: -b - d, i: 0}, {r: -b + d, i: 0}] 35 | : [-b - d, -b + d]; 36 | } else if (d === 0) { 37 | d = -b / (2 * a); 38 | return complex ? [{r: d, i: 0}] : [d]; 39 | } else { 40 | if (complex) { 41 | d = Math.sqrt(-d) / (2 * a); 42 | return [ 43 | {r: -b, i: -d}, 44 | {r: -b, i: d} 45 | ]; 46 | } 47 | return []; 48 | } 49 | } 50 | 51 | quadratic.complex = function(x) { 52 | if (!arguments.length) return complex; 53 | complex = x; 54 | return quadratic; 55 | }; 56 | 57 | return quadratic; 58 | }; 59 | // Constructs a multi-dimensional array filled with zeroes. 60 | science.zeroes = function(n) { 61 | var i = -1, 62 | a = []; 63 | if (arguments.length === 1) 64 | while (++i < n) 65 | a[i] = 0; 66 | else 67 | while (++i < n) 68 | a[i] = science.zeroes.apply( 69 | this, Array.prototype.slice.call(arguments, 1)); 70 | return a; 71 | }; 72 | science.vector = {}; 73 | science.vector.cross = function(a, b) { 74 | // TODO how to handle non-3D vectors? 75 | // TODO handle 7D vectors? 76 | return [ 77 | a[1] * b[2] - a[2] * b[1], 78 | a[2] * b[0] - a[0] * b[2], 79 | a[0] * b[1] - a[1] * b[0] 80 | ]; 81 | }; 82 | science.vector.dot = function(a, b) { 83 | var s = 0, 84 | i = -1, 85 | n = Math.min(a.length, b.length); 86 | while (++i < n) s += a[i] * b[i]; 87 | return s; 88 | }; 89 | science.vector.length = function(p) { 90 | return Math.sqrt(science.vector.dot(p, p)); 91 | }; 92 | science.vector.normalize = function(p) { 93 | var length = science.vector.length(p); 94 | return p.map(function(d) { return d / length; }); 95 | }; 96 | // 4x4 matrix determinant. 97 | science.vector.determinant = function(matrix) { 98 | var m = matrix[0].concat(matrix[1]).concat(matrix[2]).concat(matrix[3]); 99 | return ( 100 | m[12] * m[9] * m[6] * m[3] - m[8] * m[13] * m[6] * m[3] - 101 | m[12] * m[5] * m[10] * m[3] + m[4] * m[13] * m[10] * m[3] + 102 | m[8] * m[5] * m[14] * m[3] - m[4] * m[9] * m[14] * m[3] - 103 | m[12] * m[9] * m[2] * m[7] + m[8] * m[13] * m[2] * m[7] + 104 | m[12] * m[1] * m[10] * m[7] - m[0] * m[13] * m[10] * m[7] - 105 | m[8] * m[1] * m[14] * m[7] + m[0] * m[9] * m[14] * m[7] + 106 | m[12] * m[5] * m[2] * m[11] - m[4] * m[13] * m[2] * m[11] - 107 | m[12] * m[1] * m[6] * m[11] + m[0] * m[13] * m[6] * m[11] + 108 | m[4] * m[1] * m[14] * m[11] - m[0] * m[5] * m[14] * m[11] - 109 | m[8] * m[5] * m[2] * m[15] + m[4] * m[9] * m[2] * m[15] + 110 | m[8] * m[1] * m[6] * m[15] - m[0] * m[9] * m[6] * m[15] - 111 | m[4] * m[1] * m[10] * m[15] + m[0] * m[5] * m[10] * m[15]); 112 | }; 113 | // Performs in-place Gauss-Jordan elimination. 114 | // 115 | // Based on Jarno Elonen's Python version (public domain): 116 | // http://elonen.iki.fi/code/misc-notes/python-gaussj/index.html 117 | science.vector.gaussjordan = function(m, eps) { 118 | if (!eps) eps = 1e-10; 119 | 120 | var h = m.length, 121 | w = m[0].length, 122 | y = -1, 123 | y2, 124 | x; 125 | 126 | while (++y < h) { 127 | var maxrow = y; 128 | 129 | // Find max pivot. 130 | y2 = y; while (++y2 < h) { 131 | if (Math.abs(m[y2][y]) > Math.abs(m[maxrow][y])) 132 | maxrow = y2; 133 | } 134 | 135 | // Swap. 136 | var tmp = m[y]; 137 | m[y] = m[maxrow]; 138 | m[maxrow] = tmp; 139 | 140 | // Singular? 141 | if (Math.abs(m[y][y]) <= eps) return false; 142 | 143 | // Eliminate column y. 144 | y2 = y; while (++y2 < h) { 145 | var c = m[y2][y] / m[y][y]; 146 | x = y - 1; while (++x < w) { 147 | m[y2][x] -= m[y][x] * c; 148 | } 149 | } 150 | } 151 | 152 | // Backsubstitute. 153 | y = h; while (--y >= 0) { 154 | var c = m[y][y]; 155 | y2 = -1; while (++y2 < y) { 156 | x = w; while (--x >= y) { 157 | m[y2][x] -= m[y][x] * m[y2][y] / c; 158 | } 159 | } 160 | m[y][y] /= c; 161 | // Normalize row y. 162 | x = h - 1; while (++x < w) { 163 | m[y][x] /= c; 164 | } 165 | } 166 | return true; 167 | }; 168 | // Find matrix inverse using Gauss-Jordan. 169 | science.vector.inverse = function(m) { 170 | var n = m.length 171 | i = -1; 172 | 173 | // Check if the matrix is square. 174 | if (n !== m[0].length) return; 175 | 176 | // Augment with identity matrix I to get AI. 177 | m = m.map(function(row, i) { 178 | var identity = new Array(n), 179 | j = -1; 180 | while (++j < n) identity[j] = i === j ? 1 : 0; 181 | return row.concat(identity); 182 | }); 183 | 184 | // Compute IA^-1. 185 | science.vector.gaussjordan(m); 186 | 187 | // Remove identity matrix I to get A^-1. 188 | while (++i < n) { 189 | m[i] = m[i].slice(n); 190 | } 191 | 192 | return m; 193 | }; 194 | science.vector.multiply = function(a, b) { 195 | var m = a.length, 196 | n = b[0].length, 197 | p = b.length, 198 | i = -1, 199 | j, 200 | k; 201 | if (p !== a[0].length) throw {"error": "columns(a) != rows(b); " + a[0].length + " != " + p}; 202 | var ab = new Array(m); 203 | while (++i < m) { 204 | ab[i] = new Array(n); 205 | j = -1; while(++j < n) { 206 | var s = 0; 207 | k = -1; while (++k < p) s += a[i][k] * b[k][j]; 208 | ab[i][j] = s; 209 | } 210 | } 211 | return ab; 212 | }; 213 | science.vector.transpose = function(a) { 214 | var m = a.length, 215 | n = a[0].length, 216 | i = -1, 217 | j, 218 | b = new Array(n); 219 | while (++i < n) { 220 | b[i] = new Array(m); 221 | j = -1; while (++j < m) b[i][j] = a[j][i]; 222 | } 223 | return b; 224 | }; 225 | })() -------------------------------------------------------------------------------- /vendor/assets/javascripts/chart/horizon.js: -------------------------------------------------------------------------------- 1 | // Implements a horizon layout, which is a variation of a single-series 2 | // area chart where the area is folded into multiple bands. Color is used to 3 | // encode band, allowing the size of the chart to be reduced significantly 4 | // without impeding readability. This layout algorithm is based on the work of 5 | // J. Heer, N. Kong and M. Agrawala in "Sizing the Horizon: The Effects of Chart 6 | // Size and Layering on the Graphical Perception of Time Series Visualizations", 7 | // CHI 2009. http://hci.stanford.edu/publications/2009/heer-horizon-chi09.pdf 8 | d3.chart.horizon = function() { 9 | var bands = 1, // between 1 and 5, typically 10 | mode = "offset", // or mirror 11 | interpolate = "linear", // or basis, monotone, step-before, etc. 12 | x = d3_chart_horizonX, 13 | y = d3_chart_horizonY, 14 | w = 960, 15 | h = 40, 16 | duration = 0; 17 | 18 | var color = d3.scale.linear() 19 | .domain([-1, 0, 1]) 20 | .range(["#d62728", "#fff", "#1f77b4"]); 21 | 22 | // For each small multiple… 23 | function horizon(g) { 24 | g.each(function(d, i) { 25 | var g = d3.select(this), 26 | n = 2 * bands + 1, 27 | xMin = Infinity, 28 | xMax = -Infinity, 29 | yMax = -Infinity, 30 | x0, // old x-scale 31 | y0, // old y-scale 32 | id; // unique id for paths 33 | 34 | // Compute x- and y-values along with extents. 35 | var data = d.map(function(d, i) { 36 | var xv = x.call(this, d, i), 37 | yv = y.call(this, d, i); 38 | if (xv < xMin) xMin = xv; 39 | if (xv > xMax) xMax = xv; 40 | if (-yv > yMax) yMax = -yv; 41 | if (yv > yMax) yMax = yv; 42 | return [xv, yv]; 43 | }); 44 | 45 | // Compute the new x- and y-scales. 46 | var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, w]), 47 | y1 = d3.scale.linear().domain([0, yMax]).range([0, h * bands]); 48 | 49 | // Retrieve the old scales, if this is an update. 50 | if (this.__chart__) { 51 | x0 = this.__chart__.x; 52 | y0 = this.__chart__.y; 53 | id = this.__chart__.id; 54 | } else { 55 | x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range()); 56 | y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range()); 57 | id = ++d3_chart_horizonId; 58 | } 59 | 60 | // We'll use a defs to store the area path and the clip path. 61 | var defs = g.selectAll("defs") 62 | .data([data]); 63 | 64 | var defsEnter = defs.enter().append("svg:defs"); 65 | 66 | // The clip path is a simple rect. 67 | defsEnter.append("svg:clipPath") 68 | .attr("id", "d3_chart_horizon_clip" + id) 69 | .append("svg:rect") 70 | .attr("width", w) 71 | .attr("height", h); 72 | 73 | defs.select("rect").transition() 74 | .duration(duration) 75 | .attr("width", w) 76 | .attr("height", h); 77 | 78 | // The area path is rendered with our resuable d3.svg.area. 79 | defsEnter.append("svg:path") 80 | .attr("id", "d3_chart_horizon_path" + id) 81 | .attr("d", d3_chart_horizonArea 82 | .interpolate(interpolate) 83 | .x(function(d) { return x0(d[0]); }) 84 | .y0(h * bands) 85 | .y1(function(d) { return h * bands - y0(d[1]); })) 86 | .transition() 87 | .duration(duration) 88 | .attr("d", d3_chart_horizonArea 89 | .x(function(d) { return x1(d[0]); }) 90 | .y1(function(d) { return h * bands - y1(d[1]); })); 91 | 92 | defs.select("path").transition() 93 | .duration(duration) 94 | .attr("d", d3_chart_horizonArea); 95 | 96 | // We'll use a container to clip all horizon layers at once. 97 | g.selectAll("g") 98 | .data([null]) 99 | .enter().append("svg:g") 100 | .attr("clip-path", "url(#d3_chart_horizon_clip" + id + ")"); 101 | 102 | // Define the transform function based on the mode. 103 | var transform = mode == "offset" 104 | ? function(d) { return "translate(0," + (d + (d < 0) - bands) * h + ")"; } 105 | : function(d) { return (d < 0 ? "scale(1,-1)" : "") + "translate(0," + (d - bands) * h + ")"; }; 106 | 107 | // Instantiate each copy of the path with different transforms. 108 | var u = g.select("g").selectAll("use") 109 | .data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number); 110 | 111 | // TODO don't fudge the enter transition 112 | u.enter().append("svg:use") 113 | .attr("xlink:href", "#d3_chart_horizon_path" + id) 114 | .attr("transform", function(d) { return transform(d + (d > 0 ? 1 : -1)); }) 115 | .style("fill", color) 116 | .transition() 117 | .duration(duration) 118 | .attr("transform", transform); 119 | 120 | u.transition() 121 | .duration(duration) 122 | .attr("transform", transform) 123 | .style("fill", color); 124 | 125 | u.exit().transition() 126 | .duration(duration) 127 | .attr("transform", transform) 128 | .remove(); 129 | 130 | // Stash the new scales. 131 | this.__chart__ = {x: x1, y: y1, id: id}; 132 | }); 133 | d3.timer.flush(); 134 | } 135 | 136 | horizon.duration = function(x) { 137 | if (!arguments.length) return duration; 138 | duration = +x; 139 | return horizon; 140 | }; 141 | 142 | horizon.bands = function(x) { 143 | if (!arguments.length) return bands; 144 | bands = +x; 145 | color.domain([-bands, 0, bands]); 146 | return horizon; 147 | }; 148 | 149 | horizon.mode = function(x) { 150 | if (!arguments.length) return mode; 151 | mode = x + ""; 152 | return horizon; 153 | }; 154 | 155 | horizon.colors = function(x) { 156 | if (!arguments.length) return color.range(); 157 | color.range(x); 158 | return horizon; 159 | }; 160 | 161 | horizon.interpolate = function(x) { 162 | if (!arguments.length) return interpolate; 163 | interpolate = x + ""; 164 | return horizon; 165 | }; 166 | 167 | horizon.x = function(z) { 168 | if (!arguments.length) return x; 169 | x = z; 170 | return horizon; 171 | }; 172 | 173 | horizon.y = function(z) { 174 | if (!arguments.length) return y; 175 | y = z; 176 | return horizon; 177 | }; 178 | 179 | horizon.width = function(x) { 180 | if (!arguments.length) return w; 181 | w = +x; 182 | return horizon; 183 | }; 184 | 185 | horizon.height = function(x) { 186 | if (!arguments.length) return h; 187 | h = +x; 188 | return horizon; 189 | }; 190 | 191 | return horizon; 192 | }; 193 | 194 | var d3_chart_horizonArea = d3.svg.area(), 195 | d3_chart_horizonId = 0; 196 | 197 | function d3_chart_horizonX(d) { 198 | return d[0]; 199 | } 200 | 201 | function d3_chart_horizonY(d) { 202 | return d[1]; 203 | } 204 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/chart/bullet.js: -------------------------------------------------------------------------------- 1 | // Chart design based on the recommendations of Stephen Few. Implementation 2 | // based on the work of Clint Ivy, Jamie Love, and Jason Davies. 3 | // http://projects.instantcognition.com/protovis/bulletchart/ 4 | d3.chart.bullet = function() { 5 | var orient = "left", // TODO top & bottom 6 | reverse = false, 7 | duration = 0, 8 | ranges = d3_chart_bulletRanges, 9 | markers = d3_chart_bulletMarkers, 10 | measures = d3_chart_bulletMeasures, 11 | width = 380, 12 | height = 30, 13 | tickFormat = null; 14 | 15 | // For each small multiple… 16 | function bullet(g) { 17 | g.each(function(d, i) { 18 | var rangez = ranges.call(this, d, i).slice().sort(d3.descending), 19 | markerz = markers.call(this, d, i).slice().sort(d3.descending), 20 | measurez = measures.call(this, d, i).slice().sort(d3.descending), 21 | g = d3.select(this); 22 | 23 | // Compute the new x-scale. 24 | var x1 = d3.scale.linear() 25 | .domain([0, Math.max(rangez[0], markerz[0], measurez[0])]) 26 | .range(reverse ? [width, 0] : [0, width]); 27 | 28 | // Retrieve the old x-scale, if this is an update. 29 | var x0 = this.__chart__ || d3.scale.linear() 30 | .domain([0, Infinity]) 31 | .range(x1.range()); 32 | 33 | // Stash the new scale. 34 | this.__chart__ = x1; 35 | 36 | // Derive width-scales from the x-scales. 37 | var w0 = d3_chart_bulletWidth(x0), 38 | w1 = d3_chart_bulletWidth(x1); 39 | 40 | // Update the range rects. 41 | var range = g.selectAll("rect.range") 42 | .data(rangez); 43 | 44 | range.enter().append("svg:rect") 45 | .attr("class", function(d, i) { return "range s" + i; }) 46 | .attr("width", w0) 47 | .attr("height", height) 48 | .attr("x", reverse ? x0 : 0) 49 | .transition() 50 | .duration(duration) 51 | .attr("width", w1) 52 | .attr("x", reverse ? x1 : 0); 53 | 54 | range.transition() 55 | .duration(duration) 56 | .attr("x", reverse ? x1 : 0) 57 | .attr("width", w1) 58 | .attr("height", height); 59 | 60 | // Update the measure rects. 61 | var measure = g.selectAll("rect.measure") 62 | .data(measurez); 63 | 64 | measure.enter().append("svg:rect") 65 | .attr("class", function(d, i) { return "measure s" + i; }) 66 | .attr("width", w0) 67 | .attr("height", height / 3) 68 | .attr("x", reverse ? x0 : 0) 69 | .attr("y", height / 3) 70 | .transition() 71 | .duration(duration) 72 | .attr("width", w1) 73 | .attr("x", reverse ? x1 : 0); 74 | 75 | measure.transition() 76 | .duration(duration) 77 | .attr("width", w1) 78 | .attr("height", height / 3) 79 | .attr("x", reverse ? x1 : 0) 80 | .attr("y", height / 3); 81 | 82 | // Update the marker lines. 83 | var marker = g.selectAll("line.marker") 84 | .data(markerz); 85 | 86 | marker.enter().append("svg:line") 87 | .attr("class", "marker") 88 | .attr("x1", x0) 89 | .attr("x2", x0) 90 | .attr("y1", height / 6) 91 | .attr("y2", height * 5 / 6) 92 | .transition() 93 | .duration(duration) 94 | .attr("x1", x1) 95 | .attr("x2", x1); 96 | 97 | marker.transition() 98 | .duration(duration) 99 | .attr("x1", x1) 100 | .attr("x2", x1) 101 | .attr("y1", height / 6) 102 | .attr("y2", height * 5 / 6); 103 | 104 | // Compute the tick format. 105 | var format = tickFormat || x1.tickFormat(8); 106 | 107 | // Update the tick groups. 108 | var tick = g.selectAll("g.tick") 109 | .data(x1.ticks(8), function(d) { 110 | return this.textContent || format(d); 111 | }); 112 | 113 | // Initialize the ticks with the old scale, x0. 114 | var tickEnter = tick.enter().append("svg:g") 115 | .attr("class", "tick") 116 | .attr("transform", d3_chart_bulletTranslate(x0)) 117 | .style("opacity", 1e-6); 118 | 119 | tickEnter.append("svg:line") 120 | .attr("y1", height) 121 | .attr("y2", height * 7 / 6); 122 | 123 | tickEnter.append("svg:text") 124 | .attr("text-anchor", "middle") 125 | .attr("dy", "1em") 126 | .attr("y", height * 7 / 6) 127 | .text(format); 128 | 129 | // Transition the entering ticks to the new scale, x1. 130 | tickEnter.transition() 131 | .duration(duration) 132 | .attr("transform", d3_chart_bulletTranslate(x1)) 133 | .style("opacity", 1); 134 | 135 | // Transition the updating ticks to the new scale, x1. 136 | var tickUpdate = tick.transition() 137 | .duration(duration) 138 | .attr("transform", d3_chart_bulletTranslate(x1)) 139 | .style("opacity", 1); 140 | 141 | tickUpdate.select("line") 142 | .attr("y1", height) 143 | .attr("y2", height * 7 / 6); 144 | 145 | tickUpdate.select("text") 146 | .attr("y", height * 7 / 6); 147 | 148 | // Transition the exiting ticks to the new scale, x1. 149 | tick.exit().transition() 150 | .duration(duration) 151 | .attr("transform", d3_chart_bulletTranslate(x1)) 152 | .style("opacity", 1e-6) 153 | .remove(); 154 | }); 155 | d3.timer.flush(); 156 | } 157 | 158 | // left, right, top, bottom 159 | bullet.orient = function(x) { 160 | if (!arguments.length) return orient; 161 | orient = x; 162 | reverse = orient == "right" || orient == "bottom"; 163 | return bullet; 164 | }; 165 | 166 | // ranges (bad, satisfactory, good) 167 | bullet.ranges = function(x) { 168 | if (!arguments.length) return ranges; 169 | ranges = x; 170 | return bullet; 171 | }; 172 | 173 | // markers (previous, goal) 174 | bullet.markers = function(x) { 175 | if (!arguments.length) return markers; 176 | markers = x; 177 | return bullet; 178 | }; 179 | 180 | // measures (actual, forecast) 181 | bullet.measures = function(x) { 182 | if (!arguments.length) return measures; 183 | measures = x; 184 | return bullet; 185 | }; 186 | 187 | bullet.width = function(x) { 188 | if (!arguments.length) return width; 189 | width = x; 190 | return bullet; 191 | }; 192 | 193 | bullet.height = function(x) { 194 | if (!arguments.length) return height; 195 | height = x; 196 | return bullet; 197 | }; 198 | 199 | bullet.tickFormat = function(x) { 200 | if (!arguments.length) return tickFormat; 201 | tickFormat = x; 202 | return bullet; 203 | }; 204 | 205 | bullet.duration = function(x) { 206 | if (!arguments.length) return duration; 207 | duration = x; 208 | return bullet; 209 | }; 210 | 211 | return bullet; 212 | }; 213 | 214 | function d3_chart_bulletRanges(d) { 215 | return d.ranges; 216 | } 217 | 218 | function d3_chart_bulletMarkers(d) { 219 | return d.markers; 220 | } 221 | 222 | function d3_chart_bulletMeasures(d) { 223 | return d.measures; 224 | } 225 | 226 | function d3_chart_bulletTranslate(x) { 227 | return function(d) { 228 | return "translate(" + x(d) + ",0)"; 229 | }; 230 | } 231 | 232 | function d3_chart_bulletWidth(x) { 233 | var x0 = x(0); 234 | return function(d) { 235 | return Math.abs(x(d) - x0); 236 | }; 237 | } 238 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/chart/qq.js: -------------------------------------------------------------------------------- 1 | // Based on http://vis.stanford.edu/protovis/ex/qqplot.html 2 | d3.chart.qq = function() { 3 | var width = 1, 4 | height = 1, 5 | duration = 0, 6 | domain = null, 7 | tickFormat = null, 8 | n = 100, 9 | x = d3_chart_qqX, 10 | y = d3_chart_qqY; 11 | 12 | // For each small multiple… 13 | function qq(g) { 14 | g.each(function(d, i) { 15 | var g = d3.select(this), 16 | qx = d3_chart_qqQuantiles(n, x.call(this, d, i)), 17 | qy = d3_chart_qqQuantiles(n, y.call(this, d, i)), 18 | xd = domain && domain.call(this, d, i) || [d3.min(qx), d3.max(qx)], // new x-domain 19 | yd = domain && domain.call(this, d, i) || [d3.min(qy), d3.max(qy)], // new y-domain 20 | x0, // old x-scale 21 | y0; // old y-scale 22 | 23 | // Compute the new x-scale. 24 | var x1 = d3.scale.linear() 25 | .domain(xd) 26 | .range([0, width]); 27 | 28 | // Compute the new y-scale. 29 | var y1 = d3.scale.linear() 30 | .domain(yd) 31 | .range([height, 0]); 32 | 33 | // Retrieve the old scales, if this is an update. 34 | if (this.__chart__) { 35 | x0 = this.__chart__.x; 36 | y0 = this.__chart__.y; 37 | } else { 38 | x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range()); 39 | y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range()); 40 | } 41 | 42 | // Stash the new scales. 43 | this.__chart__ = {x: x1, y: y1}; 44 | 45 | // Update diagonal line. 46 | var diagonal = g.selectAll("line.diagonal") 47 | .data([null]); 48 | 49 | diagonal.enter().append("svg:line") 50 | .attr("class", "diagonal") 51 | .attr("x1", x1(yd[0])) 52 | .attr("y1", y1(xd[0])) 53 | .attr("x2", x1(yd[1])) 54 | .attr("y2", y1(xd[1])); 55 | 56 | diagonal.transition() 57 | .duration(duration) 58 | .attr("x1", x1(yd[0])) 59 | .attr("y1", y1(xd[0])) 60 | .attr("x2", x1(yd[1])) 61 | .attr("y2", y1(xd[1])); 62 | 63 | // Update quantile plots. 64 | var circle = g.selectAll("circle") 65 | .data(d3.range(n).map(function(i) { 66 | return {x: qx[i], y: qy[i]}; 67 | })); 68 | 69 | circle.enter().append("svg:circle") 70 | .attr("class", "quantile") 71 | .attr("r", 4.5) 72 | .attr("cx", function(d) { return x0(d.x); }) 73 | .attr("cy", function(d) { return y0(d.y); }) 74 | .style("opacity", 1e-6) 75 | .transition() 76 | .duration(duration) 77 | .attr("cx", function(d) { return x1(d.x); }) 78 | .attr("cy", function(d) { return y1(d.y); }) 79 | .style("opacity", 1); 80 | 81 | circle.transition() 82 | .duration(duration) 83 | .attr("cx", function(d) { return x1(d.x); }) 84 | .attr("cy", function(d) { return y1(d.y); }) 85 | .style("opacity", 1); 86 | 87 | circle.exit().transition() 88 | .duration(duration) 89 | .attr("cx", function(d) { return x1(d.x); }) 90 | .attr("cy", function(d) { return y1(d.y); }) 91 | .style("opacity", 1e-6) 92 | .remove(); 93 | 94 | var xformat = tickFormat || x1.tickFormat(4), 95 | yformat = tickFormat || y1.tickFormat(4), 96 | tx = function(d) { return "translate(" + x1(d) + "," + height + ")"; }, 97 | ty = function(d) { return "translate(0," + y1(d) + ")"; }; 98 | 99 | // Update x-ticks. 100 | var xtick = g.selectAll("g.x.tick") 101 | .data(x1.ticks(4), function(d) { 102 | return this.textContent || xformat(d); 103 | }); 104 | 105 | var xtickEnter = xtick.enter().append("svg:g") 106 | .attr("class", "x tick") 107 | .attr("transform", function(d) { return "translate(" + x0(d) + "," + height + ")"; }) 108 | .style("opacity", 1e-6); 109 | 110 | xtickEnter.append("svg:line") 111 | .attr("y1", 0) 112 | .attr("y2", -6); 113 | 114 | xtickEnter.append("svg:text") 115 | .attr("text-anchor", "middle") 116 | .attr("dy", "1em") 117 | .text(xformat); 118 | 119 | // Transition the entering ticks to the new scale, x1. 120 | xtickEnter.transition() 121 | .duration(duration) 122 | .attr("transform", tx) 123 | .style("opacity", 1); 124 | 125 | // Transition the updating ticks to the new scale, x1. 126 | xtick.transition() 127 | .duration(duration) 128 | .attr("transform", tx) 129 | .style("opacity", 1); 130 | 131 | // Transition the exiting ticks to the new scale, x1. 132 | xtick.exit().transition() 133 | .duration(duration) 134 | .attr("transform", tx) 135 | .style("opacity", 1e-6) 136 | .remove(); 137 | 138 | // Update ticks. 139 | var ytick = g.selectAll("g.y.tick") 140 | .data(y1.ticks(4), function(d) { 141 | return this.textContent || yformat(d); 142 | }); 143 | 144 | var ytickEnter = ytick.enter().append("svg:g") 145 | .attr("class", "y tick") 146 | .attr("transform", function(d) { return "translate(0," + y0(d) + ")"; }) 147 | .style("opacity", 1e-6); 148 | 149 | ytickEnter.append("svg:line") 150 | .attr("x1", 0) 151 | .attr("x2", 6); 152 | 153 | ytickEnter.append("svg:text") 154 | .attr("text-anchor", "end") 155 | .attr("dx", "-.5em") 156 | .attr("dy", ".3em") 157 | .text(yformat); 158 | 159 | // Transition the entering ticks to the new scale, y1. 160 | ytickEnter.transition() 161 | .duration(duration) 162 | .attr("transform", ty) 163 | .style("opacity", 1); 164 | 165 | // Transition the updating ticks to the new scale, y1. 166 | ytick.transition() 167 | .duration(duration) 168 | .attr("transform", ty) 169 | .style("opacity", 1); 170 | 171 | // Transition the exiting ticks to the new scale, y1. 172 | ytick.exit().transition() 173 | .duration(duration) 174 | .attr("transform", ty) 175 | .style("opacity", 1e-6) 176 | .remove(); 177 | }); 178 | } 179 | 180 | qq.width = function(x) { 181 | if (!arguments.length) return width; 182 | width = x; 183 | return qq; 184 | }; 185 | 186 | qq.height = function(x) { 187 | if (!arguments.length) return height; 188 | height = x; 189 | return qq; 190 | }; 191 | 192 | qq.duration = function(x) { 193 | if (!arguments.length) return duration; 194 | duration = x; 195 | return qq; 196 | }; 197 | 198 | qq.domain = function(x) { 199 | if (!arguments.length) return domain; 200 | domain = x == null ? x : d3.functor(x); 201 | return qq; 202 | }; 203 | 204 | qq.count = function(z) { 205 | if (!arguments.length) return n; 206 | n = z; 207 | return qq; 208 | }; 209 | 210 | qq.x = function(z) { 211 | if (!arguments.length) return x; 212 | x = z; 213 | return qq; 214 | }; 215 | 216 | qq.y = function(z) { 217 | if (!arguments.length) return y; 218 | y = z; 219 | return qq; 220 | }; 221 | 222 | qq.tickFormat = function(x) { 223 | if (!arguments.length) return tickFormat; 224 | tickFormat = x; 225 | return qq; 226 | }; 227 | 228 | return qq; 229 | }; 230 | 231 | function d3_chart_qqQuantiles(n, values) { 232 | var m = values.length - 1; 233 | values = values.slice().sort(d3.ascending); 234 | return d3.range(n).map(function(i) { 235 | return values[~~(i * m / n)]; 236 | }); 237 | } 238 | 239 | function d3_chart_qqX(d) { 240 | return d.x; 241 | } 242 | 243 | function d3_chart_qqY(d) { 244 | return d.y; 245 | } 246 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/chart/box.js: -------------------------------------------------------------------------------- 1 | // Inspired by http://informationandvisualization.de/blog/box-plot 2 | d3.chart.box = function() { 3 | var width = 1, 4 | height = 1, 5 | duration = 0, 6 | domain = null, 7 | value = Number, 8 | whiskers = d3_chart_boxWhiskers, 9 | quartiles = d3_chart_boxQuartiles, 10 | tickFormat = null; 11 | 12 | // For each small multiple… 13 | function box(g) { 14 | g.each(function(d, i) { 15 | d = d.map(value).sort(d3.ascending); 16 | var g = d3.select(this), 17 | n = d.length, 18 | min = d[0], 19 | max = d[n - 1]; 20 | 21 | // Compute quartiles. Must return exactly 3 elements. 22 | var quartileData = d.quartiles = quartiles(d); 23 | 24 | // Compute whiskers. Must return exactly 2 elements, or null. 25 | var whiskerIndices = whiskers && whiskers.call(this, d, i), 26 | whiskerData = whiskerIndices && whiskerIndices.map(function(i) { return d[i]; }); 27 | 28 | // Compute outliers. If no whiskers are specified, all data are "outliers". 29 | // We compute the outliers as indices, so that we can join across transitions! 30 | var outlierIndices = whiskerIndices 31 | ? d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n)) 32 | : d3.range(n); 33 | 34 | // Compute the new x-scale. 35 | var x1 = d3.scale.linear() 36 | .domain(domain && domain.call(this, d, i) || [min, max]) 37 | .range([height, 0]); 38 | 39 | // Retrieve the old x-scale, if this is an update. 40 | var x0 = this.__chart__ || d3.scale.linear() 41 | .domain([0, Infinity]) 42 | .range(x1.range()); 43 | 44 | // Stash the new scale. 45 | this.__chart__ = x1; 46 | 47 | // Note: the box, median, and box tick elements are fixed in number, 48 | // so we only have to handle enter and update. In contrast, the outliers 49 | // and other elements are variable, so we need to exit them! Variable 50 | // elements also fade in and out. 51 | 52 | // Update center line: the vertical line spanning the whiskers. 53 | var center = g.selectAll("line.center") 54 | .data(whiskerData ? [whiskerData] : []); 55 | 56 | center.enter().insert("svg:line", "rect") 57 | .attr("class", "center") 58 | .attr("x1", width / 2) 59 | .attr("y1", function(d) { return x0(d[0]); }) 60 | .attr("x2", width / 2) 61 | .attr("y2", function(d) { return x0(d[1]); }) 62 | .style("opacity", 1e-6) 63 | .transition() 64 | .duration(duration) 65 | .style("opacity", 1) 66 | .attr("y1", function(d) { return x1(d[0]); }) 67 | .attr("y2", function(d) { return x1(d[1]); }); 68 | 69 | center.transition() 70 | .duration(duration) 71 | .style("opacity", 1) 72 | .attr("y1", function(d) { return x1(d[0]); }) 73 | .attr("y2", function(d) { return x1(d[1]); }); 74 | 75 | center.exit().transition() 76 | .duration(duration) 77 | .style("opacity", 1e-6) 78 | .attr("y1", function(d) { return x1(d[0]); }) 79 | .attr("y2", function(d) { return x1(d[1]); }) 80 | .remove(); 81 | 82 | // Update innerquartile box. 83 | var box = g.selectAll("rect.box") 84 | .data([quartileData]); 85 | 86 | box.enter().append("svg:rect") 87 | .attr("class", "box") 88 | .attr("x", 0) 89 | .attr("y", function(d) { return x0(d[2]); }) 90 | .attr("width", width) 91 | .attr("height", function(d) { return x0(d[0]) - x0(d[2]); }) 92 | .transition() 93 | .duration(duration) 94 | .attr("y", function(d) { return x1(d[2]); }) 95 | .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); 96 | 97 | box.transition() 98 | .duration(duration) 99 | .attr("y", function(d) { return x1(d[2]); }) 100 | .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); 101 | 102 | // Update median line. 103 | var medianLine = g.selectAll("line.median") 104 | .data([quartileData[1]]); 105 | 106 | medianLine.enter().append("svg:line") 107 | .attr("class", "median") 108 | .attr("x1", 0) 109 | .attr("y1", x0) 110 | .attr("x2", width) 111 | .attr("y2", x0) 112 | .transition() 113 | .duration(duration) 114 | .attr("y1", x1) 115 | .attr("y2", x1); 116 | 117 | medianLine.transition() 118 | .duration(duration) 119 | .attr("y1", x1) 120 | .attr("y2", x1); 121 | 122 | // Update whiskers. 123 | var whisker = g.selectAll("line.whisker") 124 | .data(whiskerData || []); 125 | 126 | whisker.enter().insert("svg:line", "circle, text") 127 | .attr("class", "whisker") 128 | .attr("x1", 0) 129 | .attr("y1", x0) 130 | .attr("x2", width) 131 | .attr("y2", x0) 132 | .style("opacity", 1e-6) 133 | .transition() 134 | .duration(duration) 135 | .attr("y1", x1) 136 | .attr("y2", x1) 137 | .style("opacity", 1); 138 | 139 | whisker.transition() 140 | .duration(duration) 141 | .attr("y1", x1) 142 | .attr("y2", x1) 143 | .style("opacity", 1); 144 | 145 | whisker.exit().transition() 146 | .duration(duration) 147 | .attr("y1", x1) 148 | .attr("y2", x1) 149 | .style("opacity", 1e-6) 150 | .remove(); 151 | 152 | // Update outliers. 153 | var outlier = g.selectAll("circle.outlier") 154 | .data(outlierIndices, Number); 155 | 156 | outlier.enter().insert("svg:circle", "text") 157 | .attr("class", "outlier") 158 | .attr("r", 5) 159 | .attr("cx", width / 2) 160 | .attr("cy", function(i) { return x0(d[i]); }) 161 | .style("opacity", 1e-6) 162 | .transition() 163 | .duration(duration) 164 | .attr("cy", function(i) { return x1(d[i]); }) 165 | .style("opacity", 1); 166 | 167 | outlier.transition() 168 | .duration(duration) 169 | .attr("cy", function(i) { return x1(d[i]); }) 170 | .style("opacity", 1); 171 | 172 | outlier.exit().transition() 173 | .duration(duration) 174 | .attr("cy", function(i) { return x1(d[i]); }) 175 | .style("opacity", 1e-6) 176 | .remove(); 177 | 178 | // Compute the tick format. 179 | var format = tickFormat || x1.tickFormat(8); 180 | 181 | // Update box ticks. 182 | var boxTick = g.selectAll("text.box") 183 | .data(quartileData); 184 | 185 | boxTick.enter().append("svg:text") 186 | .attr("class", "box") 187 | .attr("dy", ".3em") 188 | .attr("dx", function(d, i) { return i & 1 ? 6 : -6 }) 189 | .attr("x", function(d, i) { return i & 1 ? width : 0 }) 190 | .attr("y", x0) 191 | .attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; }) 192 | .text(format) 193 | .transition() 194 | .duration(duration) 195 | .attr("y", x1); 196 | 197 | boxTick.transition() 198 | .duration(duration) 199 | .text(format) 200 | .attr("y", x1); 201 | 202 | // Update whisker ticks. These are handled separately from the box 203 | // ticks because they may or may not exist, and we want don't want 204 | // to join box ticks pre-transition with whisker ticks post-. 205 | var whiskerTick = g.selectAll("text.whisker") 206 | .data(whiskerData || []); 207 | 208 | whiskerTick.enter().append("svg:text") 209 | .attr("class", "whisker") 210 | .attr("dy", ".3em") 211 | .attr("dx", 6) 212 | .attr("x", width) 213 | .attr("y", x0) 214 | .text(format) 215 | .style("opacity", 1e-6) 216 | .transition() 217 | .duration(duration) 218 | .attr("y", x1) 219 | .style("opacity", 1); 220 | 221 | whiskerTick.transition() 222 | .duration(duration) 223 | .text(format) 224 | .attr("y", x1) 225 | .style("opacity", 1); 226 | 227 | whiskerTick.exit().transition() 228 | .duration(duration) 229 | .attr("y", x1) 230 | .style("opacity", 1e-6) 231 | .remove(); 232 | }); 233 | d3.timer.flush(); 234 | } 235 | 236 | box.width = function(x) { 237 | if (!arguments.length) return width; 238 | width = x; 239 | return box; 240 | }; 241 | 242 | box.height = function(x) { 243 | if (!arguments.length) return height; 244 | height = x; 245 | return box; 246 | }; 247 | 248 | box.tickFormat = function(x) { 249 | if (!arguments.length) return tickFormat; 250 | tickFormat = x; 251 | return box; 252 | }; 253 | 254 | box.duration = function(x) { 255 | if (!arguments.length) return duration; 256 | duration = x; 257 | return box; 258 | }; 259 | 260 | box.domain = function(x) { 261 | if (!arguments.length) return domain; 262 | domain = x == null ? x : d3.functor(x); 263 | return box; 264 | }; 265 | 266 | box.value = function(x) { 267 | if (!arguments.length) return value; 268 | value = x; 269 | return box; 270 | }; 271 | 272 | box.whiskers = function(x) { 273 | if (!arguments.length) return whiskers; 274 | whiskers = x; 275 | return box; 276 | }; 277 | 278 | box.quartiles = function(x) { 279 | if (!arguments.length) return quartiles; 280 | quartiles = x; 281 | return box; 282 | }; 283 | 284 | return box; 285 | }; 286 | 287 | function d3_chart_boxWhiskers(d) { 288 | return [0, d.length - 1]; 289 | } 290 | 291 | function d3_chart_boxQuartiles(d) { 292 | return [ 293 | d3.quantile(d, .25), 294 | d3.quantile(d, .5), 295 | d3.quantile(d, .75) 296 | ]; 297 | } 298 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/science.stats.js: -------------------------------------------------------------------------------- 1 | (function(){science.stats = {}; 2 | // Bandwidth selectors for Gaussian kernels. 3 | // Based on R's implementations in `stats.bw`. 4 | science.stats.bandwidth = { 5 | 6 | // Silverman, B. W. (1986) Density Estimation. London: Chapman and Hall. 7 | nrd0: function(x) { 8 | var hi = Math.sqrt(science.stats.variance(x)); 9 | if (!(lo = Math.min(hi, science.stats.iqr(x) / 1.34))) 10 | (lo = hi) || (lo = Math.abs(x[1])) || (lo = 1); 11 | return .9 * lo * Math.pow(x.length, -.2); 12 | }, 13 | 14 | // Scott, D. W. (1992) Multivariate Density Estimation: Theory, Practice, and 15 | // Visualization. Wiley. 16 | nrd: function(x) { 17 | var h = science.stats.iqr(x) / 1.34; 18 | return 1.06 * Math.min(Math.sqrt(science.stats.variance(x)), h) 19 | * Math.pow(x.length, -1/5); 20 | } 21 | }; 22 | science.stats.distance = { 23 | euclidean: function(a, b) { 24 | var n = a.length, 25 | i = -1, 26 | s = 0, 27 | x; 28 | while (++i < n) { 29 | x = a[i] - b[i]; 30 | s += x * x; 31 | } 32 | return Math.sqrt(s); 33 | }, 34 | manhattan: function(a, b) { 35 | var n = a.length, 36 | i = -1, 37 | s = 0; 38 | while (++i < n) s += Math.abs(a[i] - b[i]); 39 | return s; 40 | }, 41 | minkowski: function(p) { 42 | return function(a, b) { 43 | var n = a.length, 44 | i = -1, 45 | s = 0; 46 | while (++i < n) s += Math.pow(Math.abs(a[i] - b[i]), p); 47 | return Math.pow(s, 1 / p); 48 | }; 49 | }, 50 | chebyshev: function(a, b) { 51 | var n = a.length, 52 | i = -1, 53 | max = 0, 54 | x; 55 | while (++i < n) { 56 | x = Math.abs(a[i] - b[i]); 57 | if (x > max) max = x; 58 | } 59 | return max; 60 | }, 61 | hamming: function(a, b) { 62 | var n = a.length, 63 | i = -1, 64 | d = 0; 65 | while (++i < n) if (a[i] !== b[i]) d++; 66 | return d; 67 | }, 68 | jaccard: function(a, b) { 69 | var n = a.length, 70 | i = -1, 71 | s = 0; 72 | while (++i < n) if (a[i] === b[i]) s++; 73 | return s / n; 74 | }, 75 | braycurtis: function(a, b) { 76 | var n = a.length, 77 | i = -1, 78 | s0 = 0, 79 | s1 = 0, 80 | ai, 81 | bi; 82 | while (++i < n) { 83 | ai = a[i]; 84 | bi = b[i]; 85 | s0 += Math.abs(ai - bi); 86 | s1 += Math.abs(ai + bi); 87 | } 88 | return s0 / s1; 89 | } 90 | }; 91 | // Based on implementation in http://picomath.org/. 92 | science.stats.erf = function(x) { 93 | var a1 = 0.254829592, 94 | a2 = -0.284496736, 95 | a3 = 1.421413741, 96 | a4 = -1.453152027, 97 | a5 = 1.061405429, 98 | p = 0.3275911; 99 | 100 | // Save the sign of x 101 | var sign = x < 0 ? -1 : 1; 102 | if (x < 0) { 103 | sign = -1; 104 | x = -x; 105 | } 106 | 107 | // A&S formula 7.1.26 108 | var t = 1 / (1 + p * x); 109 | return sign * ( 110 | 1 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) 111 | * t * Math.exp(-x * x)); 112 | }; 113 | science.stats.phi = function(x) { 114 | return .5 * (1 + science.stats.erf(x / Math.SQRT2)); 115 | }; 116 | // See . 117 | science.stats.kernel = { 118 | uniform: function(u) { 119 | if (u <= 1 && u >= -1) return .5; 120 | return 0; 121 | }, 122 | triangular: function(u) { 123 | if (u <= 1 && u >= -1) return 1 - Math.abs(u); 124 | return 0; 125 | }, 126 | epanechnikov: function(u) { 127 | if (u <= 1 && u >= -1) return .75 * (1 - u * u); 128 | return 0; 129 | }, 130 | quartic: function(u) { 131 | if (u <= 1 && u >= -1) { 132 | var tmp = 1 - u * u; 133 | return (15 / 16) * tmp * tmp; 134 | } 135 | return 0; 136 | }, 137 | triweight: function(u) { 138 | if (u <= 1 && u >= -1) { 139 | var tmp = 1 - u * u; 140 | return (35 / 32) * tmp * tmp * tmp; 141 | } 142 | return 0; 143 | }, 144 | gaussian: function(u) { 145 | return 1 / Math.sqrt(2 * Math.PI) * Math.exp(-.5 * u * u); 146 | }, 147 | cosine: function(u) { 148 | if (u <= 1 && u >= -1) return Math.PI / 4 * Math.cos(Math.PI / 2 * u); 149 | return 0; 150 | } 151 | }; 152 | // http://exploringdata.net/den_trac.htm 153 | science.stats.kde = function() { 154 | var kernel = science.stats.kernel.gaussian, 155 | sample = [], 156 | bandwidth = science.stats.bandwidth.nrd; 157 | 158 | function kde(points, i) { 159 | var bw = bandwidth.call(this, sample); 160 | return points.map(function(x) { 161 | var i = -1, 162 | y = 0, 163 | n = sample.length; 164 | while (++i < n) { 165 | y += kernel((x - sample[i]) / bw); 166 | } 167 | return [x, y / bw / n]; 168 | }); 169 | } 170 | 171 | kde.kernel = function(x) { 172 | if (!arguments.length) return kernel; 173 | kernel = x; 174 | return kde; 175 | }; 176 | 177 | kde.sample = function(x) { 178 | if (!arguments.length) return sample; 179 | sample = x; 180 | return kde; 181 | }; 182 | 183 | kde.bandwidth = function(x) { 184 | if (!arguments.length) return bandwidth; 185 | bandwidth = science.functor(x); 186 | return kde; 187 | }; 188 | 189 | return kde; 190 | }; 191 | // Based on figue implementation by Jean-Yves Delort. 192 | // http://code.google.com/p/figue/ 193 | science.stats.kmeans = function() { 194 | var distance = science.stats.distance.euclidean, 195 | maxIterations = 1000, 196 | k = 1; 197 | 198 | function kmeans(vectors) { 199 | var n = vectors.length, 200 | assignments = [], 201 | clusterSizes = [], 202 | repeat = 1, 203 | iterations = 0, 204 | centroids = science_stats_kmeansRandom(k, vectors), 205 | newCentroids, 206 | i, 207 | j, 208 | x, 209 | d, 210 | min, 211 | best; 212 | 213 | while (repeat && iterations < maxIterations) { 214 | // Assignment step. 215 | j = -1; while (++j < k) { 216 | clusterSizes[j] = 0; 217 | } 218 | 219 | i = -1; while (++i < n) { 220 | x = vectors[i]; 221 | min = Infinity; 222 | j = -1; while (++j < k) { 223 | d = distance.call(this, centroids[j], x); 224 | if (d < min) { 225 | min = d; 226 | best = j; 227 | } 228 | } 229 | clusterSizes[assignments[i] = best]++; 230 | } 231 | 232 | // Update centroids step. 233 | newCentroids = []; 234 | i = -1; while (++i < n) { 235 | x = assignments[i]; 236 | d = newCentroids[x]; 237 | if (d == null) newCentroids[x] = vectors[i].slice(); 238 | else { 239 | j = -1; while (++j < d.length) { 240 | d[j] += vectors[i][j]; 241 | } 242 | } 243 | } 244 | j = -1; while (++j < k) { 245 | x = newCentroids[j]; 246 | d = 1 / clusterSizes[j]; 247 | i = -1; while (++i < x.length) x[i] *= d; 248 | } 249 | 250 | // Check convergence. 251 | repeat = 0; 252 | j = -1; while (++j < k) { 253 | if (!science_stats_kmeansCompare(newCentroids[j], centroids[j])) { 254 | repeat = 1; 255 | break; 256 | } 257 | } 258 | centroids = newCentroids; 259 | iterations++; 260 | } 261 | return {assignments: assignments, centroids: centroids}; 262 | } 263 | 264 | kmeans.k = function(x) { 265 | if (!arguments.length) return k; 266 | k = x; 267 | return kmeans; 268 | }; 269 | 270 | kmeans.distance = function(x) { 271 | if (!arguments.length) return distance; 272 | distance = x; 273 | return kmeans; 274 | }; 275 | 276 | return kmeans; 277 | }; 278 | 279 | function science_stats_kmeansCompare(a, b) { 280 | if (!a || !b || a.length !== b.length) return false; 281 | var n = a.length, 282 | i = -1; 283 | while (++i < n) if (a[i] !== b[i]) return false; 284 | return true; 285 | } 286 | 287 | // Returns an array of k distinct vectors randomly selected from the input 288 | // array of vectors. Returns null if k > n or if there are less than k distinct 289 | // objects in vectors. 290 | function science_stats_kmeansRandom(k, vectors) { 291 | var n = vectors.length; 292 | if (k > n) return null; 293 | 294 | var selected_vectors = []; 295 | var selected_indices = []; 296 | var tested_indices = {}; 297 | var tested = 0; 298 | var selected = 0; 299 | var i, 300 | vector, 301 | select; 302 | 303 | while (selected < k) { 304 | if (tested === n) return null; 305 | 306 | var random_index = Math.floor(Math.random() * n); 307 | if (random_index in tested_indices) continue; 308 | 309 | tested_indices[random_index] = 1; 310 | tested++; 311 | vector = vectors[random_index]; 312 | select = true; 313 | for (i = 0; i < selected; i++) { 314 | if (science_stats_kmeansCompare(vector, selected_vectors[i])) { 315 | select = false; 316 | break; 317 | } 318 | } 319 | if (select) { 320 | selected_vectors[selected] = vector; 321 | selected_indices[selected] = random_index; 322 | selected++; 323 | } 324 | } 325 | return selected_vectors; 326 | } 327 | science.stats.hcluster = function() { 328 | var distance = science.stats.distance.euclidean, 329 | linkage = "simple"; // simple, complete or average 330 | 331 | function hcluster(vectors) { 332 | var n = vectors.length, 333 | dMin = [], 334 | cSize = [], 335 | distMatrix = [], 336 | clusters = [], 337 | c1, 338 | c2, 339 | c1Cluster, 340 | c2Cluster, 341 | p, 342 | root, 343 | i, 344 | j; 345 | 346 | // Initialise distance matrix and vector of closest clusters. 347 | i = -1; while (++i < n) { 348 | dMin[i] = 0; 349 | distMatrix[i] = []; 350 | j = -1; while (++j < n) { 351 | distMatrix[i][j] = i === j ? Infinity : distance(vectors[i] , vectors[j]); 352 | if (distMatrix[i][dMin[i]] > distMatrix[i][j]) dMin[i] = j; 353 | } 354 | } 355 | 356 | // create leaves of the tree 357 | i = -1; while (++i < n) { 358 | clusters[i] = []; 359 | clusters[i][0] = { 360 | left: null, 361 | right: null, 362 | dist: 0, 363 | centroid: vectors[i], 364 | size: 1, 365 | depth: 0 366 | }; 367 | cSize[i] = 1; 368 | } 369 | 370 | // Main loop 371 | for (p = 0; p < n-1; p++) { 372 | // find the closest pair of clusters 373 | c1 = 0; 374 | for (i = 0; i < n; i++) { 375 | if (distMatrix[i][dMin[i]] < distMatrix[c1][dMin[c1]]) c1 = i; 376 | } 377 | c2 = dMin[c1]; 378 | 379 | // create node to store cluster info 380 | c1Cluster = clusters[c1][0]; 381 | c2Cluster = clusters[c2][0]; 382 | 383 | newCluster = { 384 | left: c1Cluster, 385 | right: c2Cluster, 386 | dist: distMatrix[c1][c2], 387 | centroid: calculateCentroid(c1Cluster.size, c1Cluster.centroid, 388 | c2Cluster.size, c2Cluster.centroid), 389 | size: c1Cluster.size + c2Cluster.size, 390 | depth: 1 + Math.max(c1Cluster.depth, c2Cluster.depth) 391 | }; 392 | clusters[c1].splice(0, 0, newCluster); 393 | cSize[c1] += cSize[c2]; 394 | 395 | // overwrite row c1 with respect to the linkage type 396 | for (j = 0; j < n; j++) { 397 | switch (linkage) { 398 | case "single": 399 | if (distMatrix[c1][j] > distMatrix[c2][j]) 400 | distMatrix[j][c1] = distMatrix[c1][j] = distMatrix[c2][j]; 401 | break; 402 | case "complete": 403 | if (distMatrix[c1][j] < distMatrix[c2][j]) 404 | distMatrix[j][c1] = distMatrix[c1][j] = distMatrix[c2][j]; 405 | break; 406 | case "average": 407 | distMatrix[j][c1] = distMatrix[c1][j] = (cSize[c1] * distMatrix[c1][j] + cSize[c2] * distMatrix[c2][j]) / (cSize[c1] + cSize[j]); 408 | break; 409 | } 410 | } 411 | distMatrix[c1][c1] = Infinity; 412 | 413 | // infinity ­out old row c2 and column c2 414 | for (i = 0; i < n; i++) 415 | distMatrix[i][c2] = distMatrix[c2][i] = Infinity; 416 | 417 | // update dmin and replace ones that previous pointed to c2 to point to c1 418 | for (j = 0; j < n; j++) { 419 | if (dMin[j] == c2) dMin[j] = c1; 420 | if (distMatrix[c1][j] < distMatrix[c1][dMin[c1]]) dMin[c1] = j; 421 | } 422 | 423 | // keep track of the last added cluster 424 | root = newCluster; 425 | } 426 | 427 | return root; 428 | } 429 | 430 | hcluster.distance = function(x) { 431 | if (!arguments.length) return distance; 432 | distance = x; 433 | return hcluster; 434 | }; 435 | 436 | return hcluster; 437 | }; 438 | 439 | function calculateCentroid(c1Size, c1Centroid, c2Size, c2Centroid) { 440 | var newCentroid = [], 441 | newSize = c1Size + c2Size, 442 | n = c1Centroid.length, 443 | i = -1; 444 | while (++i < n) { 445 | newCentroid[i] = (c1Size * c1Centroid[i] + c2Size * c2Centroid[i]) / newSize; 446 | } 447 | return newCentroid; 448 | } 449 | science.stats.iqr = function(x) { 450 | var quartiles = science.stats.quantiles(x, [.25, .75]); 451 | return quartiles[1] - quartiles[0]; 452 | }; 453 | // Based on org.apache.commons.math.analysis.interpolation.LoessInterpolator 454 | // from http://commons.apache.org/math/ 455 | science.stats.loess = function() { 456 | var bandwidth = .3, 457 | robustnessIters = 2, 458 | accuracy = 1e-12; 459 | 460 | function smooth(xval, yval, weights) { 461 | var n = xval.length, 462 | i; 463 | 464 | if (n !== yval.length) throw {error: "Mismatched array lengths"}; 465 | if (n == 0) throw {error: "At least one point required."}; 466 | 467 | if (arguments.length < 3) { 468 | weights = []; 469 | i = -1; while (++i < n) weights[i] = 1; 470 | } 471 | 472 | science_stats_loessFiniteReal(xval); 473 | science_stats_loessFiniteReal(yval); 474 | science_stats_loessFiniteReal(weights); 475 | science_stats_loessStrictlyIncreasing(xval); 476 | 477 | if (n == 1) return [yval[0]]; 478 | if (n == 2) return [yval[0], yval[1]]; 479 | 480 | var bandwidthInPoints = Math.floor(bandwidth * n); 481 | 482 | if (bandwidthInPoints < 2) throw {error: "Bandwidth too small."}; 483 | 484 | var res = [], 485 | residuals = [], 486 | robustnessWeights = []; 487 | 488 | // Do an initial fit and 'robustnessIters' robustness iterations. 489 | // This is equivalent to doing 'robustnessIters+1' robustness iterations 490 | // starting with all robustness weights set to 1. 491 | i = -1; while (++i < n) { 492 | res[i] = 0; 493 | residuals[i] = 0; 494 | robustnessWeights[i] = 1; 495 | } 496 | 497 | var iter = -1; 498 | while (++iter <= robustnessIters) { 499 | var bandwidthInterval = [0, bandwidthInPoints - 1]; 500 | // At each x, compute a local weighted linear regression 501 | var x; 502 | i = -1; while (++i < n) { 503 | x = xval[i]; 504 | 505 | // Find out the interval of source points on which 506 | // a regression is to be made. 507 | if (i > 0) { 508 | science_stats_loessUpdateBandwidthInterval(xval, weights, i, bandwidthInterval); 509 | } 510 | 511 | var ileft = bandwidthInterval[0], 512 | iright = bandwidthInterval[1]; 513 | 514 | // Compute the point of the bandwidth interval that is 515 | // farthest from x 516 | var edge = (xval[i] - xval[ileft]) > (xval[iright] - xval[i]) ? ileft : iright; 517 | 518 | // Compute a least-squares linear fit weighted by 519 | // the product of robustness weights and the tricube 520 | // weight function. 521 | // See http://en.wikipedia.org/wiki/Linear_regression 522 | // (section "Univariate linear case") 523 | // and http://en.wikipedia.org/wiki/Weighted_least_squares 524 | // (section "Weighted least squares") 525 | var sumWeights = 0, 526 | sumX = 0, 527 | sumXSquared = 0, 528 | sumY = 0, 529 | sumXY = 0, 530 | denom = Math.abs(1 / (xval[edge] - x)); 531 | 532 | for (var k = ileft; k <= iright; ++k) { 533 | var xk = xval[k], 534 | yk = yval[k], 535 | dist = k < i ? x - xk : xk - x, 536 | w = science_stats_loessTricube(dist * denom) * robustnessWeights[k] * weights[k], 537 | xkw = xk * w; 538 | sumWeights += w; 539 | sumX += xkw; 540 | sumXSquared += xk * xkw; 541 | sumY += yk * w; 542 | sumXY += yk * xkw; 543 | } 544 | 545 | var meanX = sumX / sumWeights, 546 | meanY = sumY / sumWeights, 547 | meanXY = sumXY / sumWeights, 548 | meanXSquared = sumXSquared / sumWeights; 549 | 550 | var beta = (Math.sqrt(Math.abs(meanXSquared - meanX * meanX)) < accuracy) 551 | ? 0 : ((meanXY - meanX * meanY) / (meanXSquared - meanX * meanX)); 552 | 553 | var alpha = meanY - beta * meanX; 554 | 555 | res[i] = beta * x + alpha; 556 | residuals[i] = Math.abs(yval[i] - res[i]); 557 | } 558 | 559 | // No need to recompute the robustness weights at the last 560 | // iteration, they won't be needed anymore 561 | if (iter === robustnessIters) { 562 | break; 563 | } 564 | 565 | // Recompute the robustness weights. 566 | 567 | // Find the median residual. 568 | var sortedResiduals = residuals.slice(); 569 | sortedResiduals.sort(); 570 | var medianResidual = sortedResiduals[Math.floor(n / 2)]; 571 | 572 | if (Math.abs(medianResidual) < accuracy) 573 | break; 574 | 575 | var arg, 576 | w; 577 | i = -1; while (++i < n) { 578 | arg = residuals[i] / (6 * medianResidual); 579 | robustnessWeights[i] = (arg >= 1) ? 0 : ((w = 1 - arg * arg) * w); 580 | } 581 | } 582 | 583 | return res; 584 | } 585 | 586 | smooth.bandwidth = function(x) { 587 | if (!arguments.length) return x; 588 | bandwidth = x; 589 | return smooth; 590 | }; 591 | 592 | smooth.robustnessIterations = function(x) { 593 | if (!arguments.length) return x; 594 | robustnessIters = x; 595 | return smooth; 596 | }; 597 | 598 | smooth.accuracy = function(x) { 599 | if (!arguments.length) return x; 600 | accuracy = x; 601 | return smooth; 602 | }; 603 | 604 | return smooth; 605 | }; 606 | 607 | function science_stats_loessFiniteReal(values) { 608 | var n = values.length, 609 | i = -1; 610 | 611 | while (++i < n) if (!isFinite(values[i])) return false; 612 | 613 | return true; 614 | } 615 | 616 | function science_stats_loessStrictlyIncreasing(xval) { 617 | var n = xval.length, 618 | i = 0; 619 | 620 | while (++i < n) if (xval[i - 1] >= xval[i]) return false; 621 | 622 | return true; 623 | } 624 | 625 | // Compute the tricube weight function. 626 | // http://en.wikipedia.org/wiki/Local_regression#Weight_function 627 | function science_stats_loessTricube(x) { 628 | return (x = 1 - x * x * x) * x * x; 629 | } 630 | 631 | // Given an index interval into xval that embraces a certain number of 632 | // points closest to xval[i-1], update the interval so that it embraces 633 | // the same number of points closest to xval[i], ignoring zero weights. 634 | function science_stats_loessUpdateBandwidthInterval( 635 | xval, weights, i, bandwidthInterval) { 636 | 637 | var left = bandwidthInterval[0], 638 | right = bandwidthInterval[1]; 639 | 640 | // The right edge should be adjusted if the next point to the right 641 | // is closer to xval[i] than the leftmost point of the current interval 642 | var nextRight = science_stats_loessNextNonzero(weights, right); 643 | if ((nextRight < xval.length) && (xval[nextRight] - xval[i]) < (xval[i] - xval[left])) { 644 | var nextLeft = science_stats_loessNextNonzero(weights, left); 645 | bandwidthInterval[0] = nextLeft; 646 | bandwidthInterval[1] = nextRight; 647 | } 648 | } 649 | 650 | function science_stats_loessNextNonzero(weights, i) { 651 | var j = i + 1; 652 | while (j < weights.length && weights[j] === 0) j++; 653 | return j; 654 | } 655 | // Welford's algorithm. 656 | science.stats.mean = function(x) { 657 | var n = x.length; 658 | if (n === 0) return NaN; 659 | var m = 0, 660 | i = -1; 661 | while (++i < n) m += (x[i] - m) / (i + 1); 662 | return m; 663 | }; 664 | science.stats.median = function(x) { 665 | return science.stats.quantiles(x, [.5])[0]; 666 | }; 667 | science.stats.mode = function(x) { 668 | x = x.slice().sort(science.ascending); 669 | var mode, 670 | n = x.length, 671 | i = -1, 672 | l = i, 673 | last = null, 674 | max = 0, 675 | tmp, 676 | v; 677 | while (++i < n) { 678 | if ((v = x[i]) !== last) { 679 | if ((tmp = i - l) > max) { 680 | max = tmp; 681 | mode = last; 682 | } 683 | last = v; 684 | l = i; 685 | } 686 | } 687 | return mode; 688 | }; 689 | // Uses R's quantile algorithm type=7. 690 | science.stats.quantiles = function(d, quantiles) { 691 | d = d.slice().sort(science.ascending); 692 | var n_1 = d.length - 1; 693 | return quantiles.map(function(q) { 694 | if (q === 0) return d[0]; 695 | else if (q === 1) return d[n_1]; 696 | 697 | var index = 1 + q * n_1, 698 | lo = Math.floor(index), 699 | h = index - lo, 700 | a = d[lo - 1]; 701 | 702 | return h === 0 ? a : a + h * (d[lo] - a); 703 | }); 704 | }; 705 | // Unbiased estimate of a sample's variance. 706 | // Also known as the sample variance, where the denominator is n - 1. 707 | science.stats.variance = function(x) { 708 | var n = x.length; 709 | if (n < 1) return NaN; 710 | if (n === 1) return 0; 711 | var mean = science.stats.mean(x), 712 | i = -1, 713 | s = 0; 714 | while (++i < n) { 715 | var v = x[i] - mean; 716 | s += v * v; 717 | } 718 | return s / (n - 1); 719 | }; 720 | })() -------------------------------------------------------------------------------- /vendor/assets/javascripts/d3.time.js: -------------------------------------------------------------------------------- 1 | (function(){d3.time = {}; 2 | 3 | var d3_time = Date; 4 | d3.time.format = function(template) { 5 | var n = template.length; 6 | 7 | function format(date) { 8 | var string = [], 9 | i = -1, 10 | j = 0, 11 | c, 12 | f; 13 | while (++i < n) { 14 | if (template.charCodeAt(i) == 37) { 15 | string.push( 16 | template.substring(j, i), 17 | (f = d3_time_formats[c = template.charAt(++i)]) 18 | ? f(date) : c); 19 | j = i + 1; 20 | } 21 | } 22 | string.push(template.substring(j, i)); 23 | return string.join(""); 24 | } 25 | 26 | format.parse = function(string) { 27 | var date = new d3_time(1900, 0, 1), 28 | i = d3_time_parse(date, template, string, 0); 29 | if (i != string.length) return null; 30 | if (date.hour12) { 31 | var hours = date.getHours() % 12; 32 | date.setHours(date.hour12pm ? hours + 12 : hours); 33 | } 34 | delete date.hour12; 35 | delete date.hour12pm; 36 | return date; 37 | }; 38 | 39 | format.toString = function() { 40 | return template; 41 | }; 42 | 43 | return format; 44 | }; 45 | 46 | function d3_time_parse(date, template, string, j) { 47 | var c, 48 | p, 49 | i = 0, 50 | n = template.length, 51 | m = string.length; 52 | while (i < n) { 53 | if (j >= m) return -1; 54 | c = template.charCodeAt(i++); 55 | if (c == 37) { 56 | p = d3_time_parsers[template.charAt(i++)]; 57 | if (!p || ((j = p(date, string, j)) < 0)) return -1; 58 | } else if (c != string.charCodeAt(j++)) { 59 | return -1; 60 | } 61 | } 62 | return j; 63 | } 64 | 65 | var d3_time_zfill2 = d3.format("02d"), 66 | d3_time_zfill3 = d3.format("03d"), 67 | d3_time_zfill4 = d3.format("04d"), 68 | d3_time_sfill2 = d3.format("2d"); 69 | 70 | var d3_time_formats = { 71 | a: function(d) { return d3_time_weekdays[d.getDay()].substring(0, 3); }, 72 | A: function(d) { return d3_time_weekdays[d.getDay()]; }, 73 | b: function(d) { return d3_time_months[d.getMonth()].substring(0, 3); }, 74 | B: function(d) { return d3_time_months[d.getMonth()]; }, 75 | c: d3.time.format("%a %b %e %H:%M:%S %Y"), 76 | d: function(d) { return d3_time_zfill2(d.getDate()); }, 77 | e: function(d) { return d3_time_sfill2(d.getDate()); }, 78 | H: function(d) { return d3_time_zfill2(d.getHours()); }, 79 | I: function(d) { return d3_time_zfill2(d.getHours() % 12 || 12); }, 80 | j: d3_time_dayOfYear, 81 | L: function(d) { return d3_time_zfill3(d.getMilliseconds()); }, 82 | m: function(d) { return d3_time_zfill2(d.getMonth() + 1); }, 83 | M: function(d) { return d3_time_zfill2(d.getMinutes()); }, 84 | p: function(d) { return d.getHours() >= 12 ? "PM" : "AM"; }, 85 | S: function(d) { return d3_time_zfill2(d.getSeconds()); }, 86 | U: d3_time_weekNumberSunday, 87 | w: function(d) { return d.getDay(); }, 88 | W: d3_time_weekNumberMonday, 89 | x: d3.time.format("%m/%d/%y"), 90 | X: d3.time.format("%H:%M:%S"), 91 | y: function(d) { return d3_time_zfill2(d.getFullYear() % 100); }, 92 | Y: function(d) { return d3_time_zfill4(d.getFullYear() % 10000); }, 93 | Z: d3_time_zone, 94 | "%": function(d) { return "%"; } 95 | }; 96 | 97 | var d3_time_parsers = { 98 | a: d3_time_parseWeekdayAbbrev, 99 | A: d3_time_parseWeekday, 100 | b: d3_time_parseMonthAbbrev, 101 | B: d3_time_parseMonth, 102 | c: d3_time_parseLocaleFull, 103 | d: d3_time_parseDay, 104 | e: d3_time_parseDay, 105 | H: d3_time_parseHour24, 106 | I: d3_time_parseHour12, 107 | // j: function(d, s, i) { /*TODO day of year [001,366] */ return i; }, 108 | L: d3_time_parseMilliseconds, 109 | m: d3_time_parseMonthNumber, 110 | M: d3_time_parseMinutes, 111 | p: d3_time_parseAmPm, 112 | S: d3_time_parseSeconds, 113 | // U: function(d, s, i) { /*TODO week number (sunday) [00,53] */ return i; }, 114 | // w: function(d, s, i) { /*TODO weekday [0,6] */ return i; }, 115 | // W: function(d, s, i) { /*TODO week number (monday) [00,53] */ return i; }, 116 | x: d3_time_parseLocaleDate, 117 | X: d3_time_parseLocaleTime, 118 | y: d3_time_parseYear, 119 | Y: d3_time_parseFullYear 120 | // , 121 | // Z: function(d, s, i) { /*TODO time zone */ return i; }, 122 | // "%": function(d, s, i) { /*TODO literal % */ return i; } 123 | }; 124 | 125 | // Note: weekday is validated, but does not set the date. 126 | function d3_time_parseWeekdayAbbrev(date, string, i) { 127 | return string.substring(i, i += 3).toLowerCase() in d3_time_weekdayAbbrevLookup ? i : -1; 128 | } 129 | 130 | var d3_time_weekdayAbbrevLookup = { 131 | sun: 3, 132 | mon: 3, 133 | tue: 3, 134 | wed: 3, 135 | thu: 3, 136 | fri: 3, 137 | sat: 3 138 | }; 139 | 140 | // Note: weekday is validated, but does not set the date. 141 | function d3_time_parseWeekday(date, string, i) { 142 | d3_time_weekdayRe.lastIndex = 0; 143 | var n = d3_time_weekdayRe.exec(string.substring(i, i + 10)); 144 | return n ? i += n[0].length : -1; 145 | } 146 | 147 | var d3_time_weekdayRe = /^(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)/ig; 148 | 149 | var d3_time_weekdays = [ 150 | "Sunday", 151 | "Monday", 152 | "Tuesday", 153 | "Wednesday", 154 | "Thursday", 155 | "Friday", 156 | "Saturday" 157 | ]; 158 | 159 | function d3_time_parseMonthAbbrev(date, string, i) { 160 | var n = d3_time_monthAbbrevLookup[string.substring(i, i += 3).toLowerCase()]; 161 | return n == null ? -1 : (date.setMonth(n), i); 162 | } 163 | 164 | var d3_time_monthAbbrevLookup = { 165 | jan: 0, 166 | feb: 1, 167 | mar: 2, 168 | apr: 3, 169 | may: 4, 170 | jun: 5, 171 | jul: 6, 172 | aug: 7, 173 | sep: 8, 174 | oct: 9, 175 | nov: 10, 176 | dec: 11 177 | }; 178 | 179 | function d3_time_parseMonth(date, string, i) { 180 | d3_time_monthRe.lastIndex = 0; 181 | var n = d3_time_monthRe.exec(string.substring(i, i + 12)); 182 | return n ? (date.setMonth(d3_time_monthLookup[n[0].toLowerCase()]), i += n[0].length) : -1; 183 | } 184 | 185 | var d3_time_monthRe = /^(?:January|February|March|April|May|June|July|August|September|October|November|December)/ig; 186 | 187 | var d3_time_monthLookup = { 188 | january: 0, 189 | february: 1, 190 | march: 2, 191 | april: 3, 192 | may: 4, 193 | june: 5, 194 | july: 6, 195 | august: 7, 196 | september: 8, 197 | october: 9, 198 | november: 10, 199 | december: 11 200 | }; 201 | 202 | var d3_time_months = [ 203 | "January", 204 | "February", 205 | "March", 206 | "April", 207 | "May", 208 | "June", 209 | "July", 210 | "August", 211 | "September", 212 | "October", 213 | "November", 214 | "December" 215 | ]; 216 | 217 | function d3_time_parseLocaleFull(date, string, i) { 218 | return d3_time_parse(date, d3_time_formats.c.toString(), string, i); 219 | } 220 | 221 | function d3_time_parseLocaleDate(date, string, i) { 222 | return d3_time_parse(date, d3_time_formats.x.toString(), string, i); 223 | } 224 | 225 | function d3_time_parseLocaleTime(date, string, i) { 226 | return d3_time_parse(date, d3_time_formats.X.toString(), string, i); 227 | } 228 | 229 | function d3_time_parseFullYear(date, string, i) { 230 | d3_time_numberRe.lastIndex = 0; 231 | var n = d3_time_numberRe.exec(string.substring(i, i + 4)); 232 | return n ? (date.setFullYear(n[0]), i += n[0].length) : -1; 233 | } 234 | 235 | function d3_time_parseYear(date, string, i) { 236 | d3_time_numberRe.lastIndex = 0; 237 | var n = d3_time_numberRe.exec(string.substring(i, i + 2)); 238 | return n ? (date.setFullYear(d3_time_century() + +n[0]), i += n[0].length) : -1; 239 | } 240 | 241 | function d3_time_century() { 242 | return ~~(new Date().getFullYear() / 1000) * 1000; 243 | } 244 | 245 | function d3_time_parseMonthNumber(date, string, i) { 246 | d3_time_numberRe.lastIndex = 0; 247 | var n = d3_time_numberRe.exec(string.substring(i, i + 2)); 248 | return n ? (date.setMonth(n[0] - 1), i += n[0].length) : -1; 249 | } 250 | 251 | function d3_time_parseDay(date, string, i) { 252 | d3_time_numberRe.lastIndex = 0; 253 | var n = d3_time_numberRe.exec(string.substring(i, i + 2)); 254 | return n ? (date.setDate(+n[0]), i += n[0].length) : -1; 255 | } 256 | 257 | // Note: we don't validate that the hour is in the range [0,23]. 258 | function d3_time_parseHour24(date, string, i) { 259 | d3_time_numberRe.lastIndex = 0; 260 | var n = d3_time_numberRe.exec(string.substring(i, i + 2)); 261 | return n ? (date.setHours(+n[0]), i += n[0].length) : -1; 262 | } 263 | 264 | // Note: we don't validate that the hour is in the range [1,12]. 265 | function d3_time_parseHour12(date, string, i) { 266 | date.hour12 = true; 267 | return d3_time_parseHour24(date, string, i); 268 | } 269 | 270 | function d3_time_parseMinutes(date, string, i) { 271 | d3_time_numberRe.lastIndex = 0; 272 | var n = d3_time_numberRe.exec(string.substring(i, i + 2)); 273 | return n ? (date.setMinutes(+n[0]), i += n[0].length) : -1; 274 | } 275 | 276 | function d3_time_parseSeconds(date, string, i) { 277 | d3_time_numberRe.lastIndex = 0; 278 | var n = d3_time_numberRe.exec(string.substring(i, i + 2)); 279 | return n ? (date.setSeconds(+n[0]), i += n[0].length) : -1; 280 | } 281 | 282 | function d3_time_parseMilliseconds(date, string, i) { 283 | d3_time_numberRe.lastIndex = 0; 284 | var n = d3_time_numberRe.exec(string.substring(i, i + 3)); 285 | return n ? (date.setMilliseconds(+n[0]), i += n[0].length) : -1; 286 | } 287 | 288 | // Note: we don't look at the next directive. 289 | var d3_time_numberRe = /\s*\d+/; 290 | 291 | function d3_time_parseAmPm(date, string, i) { 292 | var n = d3_time_amPmLookup[string.substring(i, i += 2).toLowerCase()]; 293 | return n == null ? -1 : (date.hour12pm = n, i); 294 | } 295 | 296 | var d3_time_amPmLookup = { 297 | am: 0, 298 | pm: 1 299 | }; 300 | 301 | function d3_time_year(d) { 302 | return new d3_time(d.getFullYear(), 0, 1); 303 | } 304 | 305 | function d3_time_daysElapsed(d0, d1) { 306 | return ~~((d1 - d0) / 864e5 - (d1.getTimezoneOffset() - d0.getTimezoneOffset()) / 1440); 307 | } 308 | 309 | function d3_time_dayOfYear(d) { 310 | return d3_time_zfill3(1 + d3_time_daysElapsed(d3_time_year(d), d)); 311 | } 312 | 313 | function d3_time_weekNumberSunday(d) { 314 | var d0 = d3_time_year(d); 315 | return d3_time_zfill2(~~((d3_time_daysElapsed(d0, d) + d0.getDay()) / 7)); 316 | } 317 | 318 | function d3_time_weekNumberMonday(d) { 319 | var d0 = d3_time_year(d); 320 | return d3_time_zfill2(~~((d3_time_daysElapsed(d0, d) + (d0.getDay() + 6) % 7) / 7)); 321 | } 322 | 323 | // TODO table of time zone offset names? 324 | function d3_time_zone(d) { 325 | var z = d.getTimezoneOffset(), 326 | zs = z > 0 ? "-" : "+", 327 | zh = ~~(Math.abs(z) / 60), 328 | zm = Math.abs(z) % 60; 329 | return zs + d3_time_zfill2(zh) + d3_time_zfill2(zm); 330 | } 331 | d3.time.format.utc = function(template) { 332 | var local = d3.time.format(template); 333 | 334 | function format(date) { 335 | try { 336 | d3_time = d3_time_format_utc; 337 | var utc = new d3_time(); 338 | utc._ = date; 339 | return local(utc); 340 | } finally { 341 | d3_time = Date; 342 | } 343 | } 344 | 345 | format.parse = function(string) { 346 | try { 347 | d3_time = d3_time_format_utc; 348 | var date = local.parse(string); 349 | return date && date._; 350 | } finally { 351 | d3_time = Date; 352 | } 353 | }; 354 | 355 | format.toString = local.toString; 356 | 357 | return format; 358 | }; 359 | 360 | function d3_time_format_utc() { 361 | this._ = new Date(Date.UTC.apply(this, arguments)); 362 | } 363 | 364 | d3_time_format_utc.prototype = { 365 | getDate: function() { return this._.getUTCDate(); }, 366 | getDay: function() { return this._.getUTCDay(); }, 367 | getFullYear: function() { return this._.getUTCFullYear(); }, 368 | getHours: function() { return this._.getUTCHours(); }, 369 | getMilliseconds: function() { return this._.getUTCMilliseconds(); }, 370 | getMinutes: function() { return this._.getUTCMinutes(); }, 371 | getMonth: function() { return this._.getUTCMonth(); }, 372 | getSeconds: function() { return this._.getUTCSeconds(); }, 373 | getTimezoneOffset: function() { return 0; }, 374 | valueOf: function() { return this._.getTime(); }, 375 | setDate: function(x) { this._.setUTCDate(x); }, 376 | setDay: function(x) { this._.setUTCDay(x); }, 377 | setFullYear: function(x) { this._.setUTCFullYear(x); }, 378 | setHours: function(x) { this._.setUTCHours(x); }, 379 | setMilliseconds: function(x) { this._.setUTCMilliseconds(x); }, 380 | setMinutes: function(x) { this._.setUTCMinutes(x); }, 381 | setMonth: function(x) { this._.setUTCMonth(x); }, 382 | setSeconds: function(x) { this._.setUTCSeconds(x); } 383 | }; 384 | var d3_time_formatIso = d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ"); 385 | 386 | d3.time.format.iso = Date.prototype.toISOString ? d3_time_formatIsoNative : d3_time_formatIso; 387 | 388 | function d3_time_formatIsoNative(date) { 389 | return date.toISOString(); 390 | } 391 | 392 | d3_time_formatIsoNative.parse = function(string) { 393 | return new Date(string); 394 | }; 395 | 396 | d3_time_formatIsoNative.toString = d3_time_formatIso.toString; 397 | function d3_time_range(floor, step, number) { 398 | return function(t0, t1, dt) { 399 | var time = floor(t0), times = []; 400 | if (time < t0) step(time); 401 | if (dt > 1) { 402 | while (time < t1) { 403 | var date = new Date(+time); 404 | if (!(number(date) % dt)) times.push(date); 405 | step(time); 406 | } 407 | } else { 408 | while (time < t1) times.push(new Date(+time)), step(time); 409 | } 410 | return times; 411 | }; 412 | } 413 | d3.time.second = function(date) { 414 | return new Date(~~(date / 1e3) * 1e3); 415 | }; 416 | 417 | d3.time.second.utc = d3.time.second; 418 | d3.time.seconds = d3_time_range(d3.time.second, function(date) { 419 | date.setTime(date.getTime() + 1e3); 420 | }, function(date) { 421 | return date.getSeconds(); 422 | }); 423 | 424 | d3.time.seconds.utc = d3.time.seconds; 425 | d3.time.minute = function(date) { 426 | return new Date(~~(date / 6e4) * 6e4); 427 | }; 428 | 429 | d3.time.minute.utc = d3.time.minute;d3.time.minutes = d3_time_range(d3.time.minute, d3_time_minutesStep, function(date) { 430 | return date.getMinutes(); 431 | }); 432 | 433 | d3.time.minutes.utc = d3_time_range(d3.time.minute, d3_time_minutesStep, function(date) { 434 | return date.getUTCMinutes(); 435 | }); 436 | 437 | function d3_time_minutesStep(date) { 438 | date.setTime(date.getTime() + 6e4); // assumes no leap seconds 439 | } 440 | d3.time.hour = function(date) { 441 | var offset = date.getTimezoneOffset() / 60; 442 | return new Date((~~(date / 36e5 - offset) + offset) * 36e5); 443 | }; 444 | 445 | d3.time.hour.utc = function(date) { 446 | return new Date(~~(date / 36e5) * 36e5); 447 | }; 448 | d3.time.hours = d3_time_range(d3.time.hour, d3_time_hoursStep, function(date) { 449 | return date.getHours(); 450 | }); 451 | 452 | d3.time.hours.utc = d3_time_range(d3.time.hour.utc, d3_time_hoursStep, function(date) { 453 | return date.getUTCHours(); 454 | }); 455 | 456 | function d3_time_hoursStep(date) { 457 | date.setTime(date.getTime() + 36e5); 458 | } 459 | d3.time.day = function(date) { 460 | return new Date(date.getFullYear(), date.getMonth(), date.getDate()); 461 | }; 462 | 463 | d3.time.day.utc = function(date) { 464 | return new Date(~~(date / 864e5) * 864e5); 465 | }; 466 | d3.time.days = d3_time_range(d3.time.day, function(date) { 467 | date.setDate(date.getDate() + 1); 468 | }, function(date) { 469 | return date.getDate() - 1; 470 | }); 471 | 472 | d3.time.days.utc = d3_time_range(d3.time.day.utc, function(date) { 473 | date.setUTCDate(date.getUTCDate() + 1); 474 | }, function(date) { 475 | return date.getUTCDate() - 1; 476 | }); 477 | d3.time.week = function(date) { 478 | (date = d3.time.day(date)).setDate(date.getDate() - date.getDay()); 479 | return date; 480 | }; 481 | 482 | d3.time.week.utc = function(date) { 483 | (date = d3.time.day.utc(date)).setUTCDate(date.getUTCDate() - date.getUTCDay()); 484 | return date; 485 | }; 486 | d3.time.weeks = d3_time_range(d3.time.week, function(date) { 487 | date.setDate(date.getDate() + 7); 488 | }, function(date) { 489 | return ~~((date - new Date(date.getFullYear(), 0, 1)) / 6048e5); 490 | }); 491 | 492 | d3.time.weeks.utc = d3_time_range(d3.time.week.utc, function(date) { 493 | date.setUTCDate(date.getUTCDate() + 7); 494 | }, function(date) { 495 | return ~~((date - Date.UTC(date.getUTCFullYear(), 0, 1)) / 6048e5); 496 | }); 497 | d3.time.month = function(date) { 498 | return new Date(date.getFullYear(), date.getMonth(), 1); 499 | }; 500 | 501 | d3.time.month.utc = function(date) { 502 | return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1)); 503 | }; 504 | d3.time.months = d3_time_range(d3.time.month, function(date) { 505 | date.setMonth(date.getMonth() + 1); 506 | }, function(date) { 507 | return date.getMonth(); 508 | }); 509 | 510 | d3.time.months.utc = d3_time_range(d3.time.month.utc, function(date) { 511 | date.setUTCMonth(date.getUTCMonth() + 1); 512 | }, function(date) { 513 | return date.getUTCMonth(); 514 | }); 515 | d3.time.year = function(date) { 516 | return new Date(date.getFullYear(), 0, 1); 517 | }; 518 | 519 | d3.time.year.utc = function(date) { 520 | return new Date(Date.UTC(date.getUTCFullYear(), 0, 1)); 521 | }; 522 | d3.time.years = d3_time_range(d3.time.year, function(date) { 523 | date.setFullYear(date.getFullYear() + 1); 524 | }, function(date) { 525 | return date.getFullYear(); 526 | }); 527 | 528 | d3.time.years.utc = d3_time_range(d3.time.year.utc, function(date) { 529 | date.setUTCFullYear(date.getUTCFullYear() + 1); 530 | }, function(date) { 531 | return date.getUTCFullYear(); 532 | }); 533 | // TODO nice 534 | function d3_time_scale(linear, methods, format) { 535 | 536 | function scale(x) { 537 | return linear(x); 538 | } 539 | 540 | scale.invert = function(x) { 541 | return d3_time_scaleDate(linear.invert(x)); 542 | }; 543 | 544 | scale.domain = function(x) { 545 | if (!arguments.length) return linear.domain().map(d3_time_scaleDate); 546 | linear.domain(x); 547 | return scale; 548 | }; 549 | 550 | scale.ticks = function(m, k) { 551 | var extent = d3_time_scaleExtent(scale.domain()); 552 | if (typeof m !== "function") { 553 | var span = extent[1] - extent[0], 554 | target = span / m, 555 | i = d3.bisect(d3_time_scaleSteps, target); 556 | if (i == d3_time_scaleSteps.length) return methods.year(extent, m); 557 | if (!i) return linear.ticks(m).map(d3_time_scaleDate); 558 | if (Math.log(target / d3_time_scaleSteps[i - 1]) < Math.log(d3_time_scaleSteps[i] / target)) --i; 559 | m = methods[i]; 560 | k = m[1]; 561 | m = m[0]; 562 | } 563 | return m(extent[0], new Date(+extent[1] + 1), k); // inclusive upper bound 564 | }; 565 | 566 | scale.tickFormat = function() { 567 | return format; 568 | }; 569 | 570 | scale.copy = function() { 571 | return d3_time_scale(linear.copy(), methods, format); 572 | }; 573 | 574 | // TOOD expose d3_scale_linear_rebind? 575 | return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); 576 | } 577 | 578 | // TODO expose d3_scaleExtent? 579 | function d3_time_scaleExtent(domain) { 580 | var start = domain[0], stop = domain[domain.length - 1]; 581 | return start < stop ? [start, stop] : [stop, start]; 582 | } 583 | 584 | function d3_time_scaleDate(t) { 585 | return new Date(t); 586 | } 587 | 588 | function d3_time_scaleFormat(formats) { 589 | return function(date) { 590 | var i = formats.length - 1, f = formats[i]; 591 | while (!f[1](date)) f = formats[--i]; 592 | return f[0](date); 593 | }; 594 | } 595 | 596 | function d3_time_scaleSetYear(y) { 597 | var d = new Date(y, 0, 1); 598 | d.setFullYear(y); // Y2K fail 599 | return d; 600 | } 601 | 602 | function d3_time_scaleGetYear(d) { 603 | var y = d.getFullYear(), 604 | d0 = d3_time_scaleSetYear(y), 605 | d1 = d3_time_scaleSetYear(y + 1); 606 | return y + (d - d0) / (d1 - d0); 607 | } 608 | 609 | var d3_time_scaleSteps = [ 610 | 1e3, // 1-second 611 | 5e3, // 5-second 612 | 15e3, // 15-second 613 | 3e4, // 30-second 614 | 6e4, // 1-minute 615 | 3e5, // 5-minute 616 | 9e5, // 15-minute 617 | 18e5, // 30-minute 618 | 36e5, // 1-hour 619 | 108e5, // 3-hour 620 | 216e5, // 6-hour 621 | 432e5, // 12-hour 622 | 864e5, // 1-day 623 | 1728e5, // 2-day 624 | 6048e5, // 1-week 625 | 2592e6, // 1-month 626 | 7776e6, // 3-month 627 | 31536e6 // 1-year 628 | ]; 629 | 630 | var d3_time_scaleLocalMethods = [ 631 | [d3.time.seconds, 1], 632 | [d3.time.seconds, 5], 633 | [d3.time.seconds, 15], 634 | [d3.time.seconds, 30], 635 | [d3.time.minutes, 1], 636 | [d3.time.minutes, 5], 637 | [d3.time.minutes, 15], 638 | [d3.time.minutes, 30], 639 | [d3.time.hours, 1], 640 | [d3.time.hours, 3], 641 | [d3.time.hours, 6], 642 | [d3.time.hours, 12], 643 | [d3.time.days, 1], 644 | [d3.time.days, 2], 645 | [d3.time.weeks, 1], 646 | [d3.time.months, 1], 647 | [d3.time.months, 3], 648 | [d3.time.years, 1] 649 | ]; 650 | 651 | var d3_time_scaleLocalFormats = [ 652 | [d3.time.format("%Y"), function(d) { return true; }], 653 | [d3.time.format("%B"), function(d) { return d.getMonth(); }], 654 | [d3.time.format("%b %d"), function(d) { return d.getDate() != 1; }], 655 | [d3.time.format("%a %d"), function(d) { return d.getDay() && d.getDate() != 1; }], 656 | [d3.time.format("%I %p"), function(d) { return d.getHours(); }], 657 | [d3.time.format("%I:%M"), function(d) { return d.getMinutes(); }], 658 | [d3.time.format(":%S"), function(d) { return d.getSeconds(); }], 659 | [d3.time.format(".%L"), function(d) { return d.getMilliseconds(); }] 660 | ]; 661 | 662 | var d3_time_scaleLinear = d3.scale.linear(), 663 | d3_time_scaleLocalFormat = d3_time_scaleFormat(d3_time_scaleLocalFormats); 664 | 665 | d3_time_scaleLocalMethods.year = function(extent, m) { 666 | return d3_time_scaleLinear.domain(extent.map(d3_time_scaleGetYear)).ticks(m).map(d3_time_scaleSetYear); 667 | }; 668 | 669 | d3.time.scale = function() { 670 | return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); 671 | }; 672 | var d3_time_scaleUTCMethods = [ 673 | [d3.time.seconds.utc, 1], 674 | [d3.time.seconds.utc, 5], 675 | [d3.time.seconds.utc, 15], 676 | [d3.time.seconds.utc, 30], 677 | [d3.time.minutes.utc, 1], 678 | [d3.time.minutes.utc, 5], 679 | [d3.time.minutes.utc, 15], 680 | [d3.time.minutes.utc, 30], 681 | [d3.time.hours.utc, 1], 682 | [d3.time.hours.utc, 3], 683 | [d3.time.hours.utc, 6], 684 | [d3.time.hours.utc, 12], 685 | [d3.time.days.utc, 1], 686 | [d3.time.days.utc, 2], 687 | [d3.time.weeks.utc, 1], 688 | [d3.time.months.utc, 1], 689 | [d3.time.months.utc, 3], 690 | [d3.time.years.utc, 1] 691 | ]; 692 | 693 | var d3_time_scaleUTCFormats = [ 694 | [d3.time.format.utc("%Y"), function(d) { return true; }], 695 | [d3.time.format.utc("%B"), function(d) { return d.getUTCMonth(); }], 696 | [d3.time.format.utc("%b %d"), function(d) { return d.getUTCDate() != 1; }], 697 | [d3.time.format.utc("%a %d"), function(d) { return d.getUTCDay() && d.getUTCDate() != 1; }], 698 | [d3.time.format.utc("%I %p"), function(d) { return d.getUTCHours(); }], 699 | [d3.time.format.utc("%I:%M"), function(d) { return d.getUTCMinutes(); }], 700 | [d3.time.format.utc(":%S"), function(d) { return d.getUTCSeconds(); }], 701 | [d3.time.format.utc(".%L"), function(d) { return d.getUTCMilliseconds(); }] 702 | ]; 703 | 704 | var d3_time_scaleUTCFormat = d3_time_scaleFormat(d3_time_scaleUTCFormats); 705 | 706 | function d3_time_scaleUTCSetYear(y) { 707 | var d = new Date(Date.UTC(y, 0, 1)); 708 | d.setUTCFullYear(y); // Y2K fail 709 | return d; 710 | } 711 | 712 | function d3_time_scaleUTCGetYear(d) { 713 | var y = d.getUTCFullYear(), 714 | d0 = d3_time_scaleUTCSetYear(y), 715 | d1 = d3_time_scaleUTCSetYear(y + 1); 716 | return y + (d - d0) / (d1 - d0); 717 | } 718 | 719 | d3_time_scaleUTCMethods.year = function(extent, m) { 720 | return d3_time_scaleLinear.domain(extent.map(d3_time_scaleUTCGetYear)).ticks(m).map(d3_time_scaleUTCSetYear); 721 | }; 722 | 723 | d3.time.scale.utc = function() { 724 | return d3_time_scale(d3.scale.linear(), d3_time_scaleUTCMethods, d3_time_scaleUTCFormat); 725 | }; 726 | })(); 727 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/colorbrewer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This product includes color specifications and designs developed by Cynthia 3 | * Brewer (http://colorbrewer.org/). 4 | */ 5 | var colorbrewer = { 6 | YlGn:{3:["rgb(247,252,185)","rgb(173,221,142)","rgb(49,163,84)"],4:["rgb(255,255,204)","rgb(194,230,153)","rgb(120,198,121)","rgb(35,132,67)"],5:["rgb(255,255,204)","rgb(194,230,153)","rgb(120,198,121)","rgb(49,163,84)","rgb(0,104,55)"],6:["rgb(255,255,204)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(49,163,84)","rgb(0,104,55)"],7:["rgb(255,255,204)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,90,50)"],8:["rgb(255,255,229)","rgb(247,252,185)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,90,50)"],9:["rgb(255,255,229)","rgb(247,252,185)","rgb(217,240,163)","rgb(173,221,142)","rgb(120,198,121)","rgb(65,171,93)","rgb(35,132,67)","rgb(0,104,55)","rgb(0,69,41)"]}, 7 | YlGnBu:{3:["rgb(237,248,177)","rgb(127,205,187)","rgb(44,127,184)"],4:["rgb(255,255,204)","rgb(161,218,180)","rgb(65,182,196)","rgb(34,94,168)"],5:["rgb(255,255,204)","rgb(161,218,180)","rgb(65,182,196)","rgb(44,127,184)","rgb(37,52,148)"],6:["rgb(255,255,204)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(44,127,184)","rgb(37,52,148)"],7:["rgb(255,255,204)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(12,44,132)"],8:["rgb(255,255,217)","rgb(237,248,177)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(12,44,132)"],9:["rgb(255,255,217)","rgb(237,248,177)","rgb(199,233,180)","rgb(127,205,187)","rgb(65,182,196)","rgb(29,145,192)","rgb(34,94,168)","rgb(37,52,148)","rgb(8,29,88)"]}, 8 | GnBu:{3:["rgb(224,243,219)","rgb(168,221,181)","rgb(67,162,202)"],4:["rgb(240,249,232)","rgb(186,228,188)","rgb(123,204,196)","rgb(43,140,190)"],5:["rgb(240,249,232)","rgb(186,228,188)","rgb(123,204,196)","rgb(67,162,202)","rgb(8,104,172)"],6:["rgb(240,249,232)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(67,162,202)","rgb(8,104,172)"],7:["rgb(240,249,232)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,88,158)"],8:["rgb(247,252,240)","rgb(224,243,219)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,88,158)"],9:["rgb(247,252,240)","rgb(224,243,219)","rgb(204,235,197)","rgb(168,221,181)","rgb(123,204,196)","rgb(78,179,211)","rgb(43,140,190)","rgb(8,104,172)","rgb(8,64,129)"]}, 9 | BuGn:{3:["rgb(229,245,249)","rgb(153,216,201)","rgb(44,162,95)"],4:["rgb(237,248,251)","rgb(178,226,226)","rgb(102,194,164)","rgb(35,139,69)"],5:["rgb(237,248,251)","rgb(178,226,226)","rgb(102,194,164)","rgb(44,162,95)","rgb(0,109,44)"],6:["rgb(237,248,251)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(44,162,95)","rgb(0,109,44)"],7:["rgb(237,248,251)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,88,36)"],8:["rgb(247,252,253)","rgb(229,245,249)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,88,36)"],9:["rgb(247,252,253)","rgb(229,245,249)","rgb(204,236,230)","rgb(153,216,201)","rgb(102,194,164)","rgb(65,174,118)","rgb(35,139,69)","rgb(0,109,44)","rgb(0,68,27)"]}, 10 | PuBuGn:{3:["rgb(236,226,240)","rgb(166,189,219)","rgb(28,144,153)"],4:["rgb(246,239,247)","rgb(189,201,225)","rgb(103,169,207)","rgb(2,129,138)"],5:["rgb(246,239,247)","rgb(189,201,225)","rgb(103,169,207)","rgb(28,144,153)","rgb(1,108,89)"],6:["rgb(246,239,247)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(28,144,153)","rgb(1,108,89)"],7:["rgb(246,239,247)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,100,80)"],8:["rgb(255,247,251)","rgb(236,226,240)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,100,80)"],9:["rgb(255,247,251)","rgb(236,226,240)","rgb(208,209,230)","rgb(166,189,219)","rgb(103,169,207)","rgb(54,144,192)","rgb(2,129,138)","rgb(1,108,89)","rgb(1,70,54)"]}, 11 | PuBu:{3:["rgb(236,231,242)","rgb(166,189,219)","rgb(43,140,190)"],4:["rgb(241,238,246)","rgb(189,201,225)","rgb(116,169,207)","rgb(5,112,176)"],5:["rgb(241,238,246)","rgb(189,201,225)","rgb(116,169,207)","rgb(43,140,190)","rgb(4,90,141)"],6:["rgb(241,238,246)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(43,140,190)","rgb(4,90,141)"],7:["rgb(241,238,246)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(3,78,123)"],8:["rgb(255,247,251)","rgb(236,231,242)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(3,78,123)"],9:["rgb(255,247,251)","rgb(236,231,242)","rgb(208,209,230)","rgb(166,189,219)","rgb(116,169,207)","rgb(54,144,192)","rgb(5,112,176)","rgb(4,90,141)","rgb(2,56,88)"]}, 12 | BuPu:{3:["rgb(224,236,244)","rgb(158,188,218)","rgb(136,86,167)"],4:["rgb(237,248,251)","rgb(179,205,227)","rgb(140,150,198)","rgb(136,65,157)"],5:["rgb(237,248,251)","rgb(179,205,227)","rgb(140,150,198)","rgb(136,86,167)","rgb(129,15,124)"],6:["rgb(237,248,251)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(136,86,167)","rgb(129,15,124)"],7:["rgb(237,248,251)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(110,1,107)"],8:["rgb(247,252,253)","rgb(224,236,244)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(110,1,107)"],9:["rgb(247,252,253)","rgb(224,236,244)","rgb(191,211,230)","rgb(158,188,218)","rgb(140,150,198)","rgb(140,107,177)","rgb(136,65,157)","rgb(129,15,124)","rgb(77,0,75)"]}, 13 | RdPu:{3:["rgb(253,224,221)","rgb(250,159,181)","rgb(197,27,138)"],4:["rgb(254,235,226)","rgb(251,180,185)","rgb(247,104,161)","rgb(174,1,126)"],5:["rgb(254,235,226)","rgb(251,180,185)","rgb(247,104,161)","rgb(197,27,138)","rgb(122,1,119)"],6:["rgb(254,235,226)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(197,27,138)","rgb(122,1,119)"],7:["rgb(254,235,226)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)"],8:["rgb(255,247,243)","rgb(253,224,221)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)"],9:["rgb(255,247,243)","rgb(253,224,221)","rgb(252,197,192)","rgb(250,159,181)","rgb(247,104,161)","rgb(221,52,151)","rgb(174,1,126)","rgb(122,1,119)","rgb(73,0,106)"]}, 14 | PuRd:{3:["rgb(231,225,239)","rgb(201,148,199)","rgb(221,28,119)"],4:["rgb(241,238,246)","rgb(215,181,216)","rgb(223,101,176)","rgb(206,18,86)"],5:["rgb(241,238,246)","rgb(215,181,216)","rgb(223,101,176)","rgb(221,28,119)","rgb(152,0,67)"],6:["rgb(241,238,246)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(221,28,119)","rgb(152,0,67)"],7:["rgb(241,238,246)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(145,0,63)"],8:["rgb(247,244,249)","rgb(231,225,239)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(145,0,63)"],9:["rgb(247,244,249)","rgb(231,225,239)","rgb(212,185,218)","rgb(201,148,199)","rgb(223,101,176)","rgb(231,41,138)","rgb(206,18,86)","rgb(152,0,67)","rgb(103,0,31)"]}, 15 | OrRd:{3:["rgb(254,232,200)","rgb(253,187,132)","rgb(227,74,51)"],4:["rgb(254,240,217)","rgb(253,204,138)","rgb(252,141,89)","rgb(215,48,31)"],5:["rgb(254,240,217)","rgb(253,204,138)","rgb(252,141,89)","rgb(227,74,51)","rgb(179,0,0)"],6:["rgb(254,240,217)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(227,74,51)","rgb(179,0,0)"],7:["rgb(254,240,217)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(153,0,0)"],8:["rgb(255,247,236)","rgb(254,232,200)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(153,0,0)"],9:["rgb(255,247,236)","rgb(254,232,200)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(179,0,0)","rgb(127,0,0)"]}, 16 | YlOrRd:{3:["rgb(255,237,160)","rgb(254,178,76)","rgb(240,59,32)"],4:["rgb(255,255,178)","rgb(254,204,92)","rgb(253,141,60)","rgb(227,26,28)"],5:["rgb(255,255,178)","rgb(254,204,92)","rgb(253,141,60)","rgb(240,59,32)","rgb(189,0,38)"],6:["rgb(255,255,178)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(240,59,32)","rgb(189,0,38)"],7:["rgb(255,255,178)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(177,0,38)"],8:["rgb(255,255,204)","rgb(255,237,160)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(177,0,38)"],9:["rgb(255,255,204)","rgb(255,237,160)","rgb(254,217,118)","rgb(254,178,76)","rgb(253,141,60)","rgb(252,78,42)","rgb(227,26,28)","rgb(189,0,38)","rgb(128,0,38)"]}, 17 | YlOrBr:{3:["rgb(255,247,188)","rgb(254,196,79)","rgb(217,95,14)"],4:["rgb(255,255,212)","rgb(254,217,142)","rgb(254,153,41)","rgb(204,76,2)"],5:["rgb(255,255,212)","rgb(254,217,142)","rgb(254,153,41)","rgb(217,95,14)","rgb(153,52,4)"],6:["rgb(255,255,212)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(217,95,14)","rgb(153,52,4)"],7:["rgb(255,255,212)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(140,45,4)"],8:["rgb(255,255,229)","rgb(255,247,188)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(140,45,4)"],9:["rgb(255,255,229)","rgb(255,247,188)","rgb(254,227,145)","rgb(254,196,79)","rgb(254,153,41)","rgb(236,112,20)","rgb(204,76,2)","rgb(153,52,4)","rgb(102,37,6)"]}, 18 | Purples:{3:["rgb(239,237,245)","rgb(188,189,220)","rgb(117,107,177)"],4:["rgb(242,240,247)","rgb(203,201,226)","rgb(158,154,200)","rgb(106,81,163)"],5:["rgb(242,240,247)","rgb(203,201,226)","rgb(158,154,200)","rgb(117,107,177)","rgb(84,39,143)"],6:["rgb(242,240,247)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(117,107,177)","rgb(84,39,143)"],7:["rgb(242,240,247)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(74,20,134)"],8:["rgb(252,251,253)","rgb(239,237,245)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(74,20,134)"],9:["rgb(252,251,253)","rgb(239,237,245)","rgb(218,218,235)","rgb(188,189,220)","rgb(158,154,200)","rgb(128,125,186)","rgb(106,81,163)","rgb(84,39,143)","rgb(63,0,125)"]}, 19 | Blues:{3:["rgb(222,235,247)","rgb(158,202,225)","rgb(49,130,189)"],4:["rgb(239,243,255)","rgb(189,215,231)","rgb(107,174,214)","rgb(33,113,181)"],5:["rgb(239,243,255)","rgb(189,215,231)","rgb(107,174,214)","rgb(49,130,189)","rgb(8,81,156)"],6:["rgb(239,243,255)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(49,130,189)","rgb(8,81,156)"],7:["rgb(239,243,255)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,69,148)"],8:["rgb(247,251,255)","rgb(222,235,247)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,69,148)"],9:["rgb(247,251,255)","rgb(222,235,247)","rgb(198,219,239)","rgb(158,202,225)","rgb(107,174,214)","rgb(66,146,198)","rgb(33,113,181)","rgb(8,81,156)","rgb(8,48,107)"]}, 20 | Greens:{3:["rgb(229,245,224)","rgb(161,217,155)","rgb(49,163,84)"],4:["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(35,139,69)"],5:["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"],6:["rgb(237,248,233)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"],7:["rgb(237,248,233)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,90,50)"],8:["rgb(247,252,245)","rgb(229,245,224)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,90,50)"],9:["rgb(247,252,245)","rgb(229,245,224)","rgb(199,233,192)","rgb(161,217,155)","rgb(116,196,118)","rgb(65,171,93)","rgb(35,139,69)","rgb(0,109,44)","rgb(0,68,27)"]}, 21 | Oranges:{3:["rgb(254,230,206)","rgb(253,174,107)","rgb(230,85,13)"],4:["rgb(254,237,222)","rgb(253,190,133)","rgb(253,141,60)","rgb(217,71,1)"],5:["rgb(254,237,222)","rgb(253,190,133)","rgb(253,141,60)","rgb(230,85,13)","rgb(166,54,3)"],6:["rgb(254,237,222)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(230,85,13)","rgb(166,54,3)"],7:["rgb(254,237,222)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(140,45,4)"],8:["rgb(255,245,235)","rgb(254,230,206)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(140,45,4)"],9:["rgb(255,245,235)","rgb(254,230,206)","rgb(253,208,162)","rgb(253,174,107)","rgb(253,141,60)","rgb(241,105,19)","rgb(217,72,1)","rgb(166,54,3)","rgb(127,39,4)"]}, 22 | Reds:{3:["rgb(254,224,210)","rgb(252,146,114)","rgb(222,45,38)"],4:["rgb(254,229,217)","rgb(252,174,145)","rgb(251,106,74)","rgb(203,24,29)"],5:["rgb(254,229,217)","rgb(252,174,145)","rgb(251,106,74)","rgb(222,45,38)","rgb(165,15,21)"],6:["rgb(254,229,217)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(222,45,38)","rgb(165,15,21)"],7:["rgb(254,229,217)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(153,0,13)"],8:["rgb(255,245,240)","rgb(254,224,210)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(153,0,13)"],9:["rgb(255,245,240)","rgb(254,224,210)","rgb(252,187,161)","rgb(252,146,114)","rgb(251,106,74)","rgb(239,59,44)","rgb(203,24,29)","rgb(165,15,21)","rgb(103,0,13)"]}, 23 | Greys:{3:["rgb(240,240,240)","rgb(189,189,189)","rgb(99,99,99)"],4:["rgb(247,247,247)","rgb(204,204,204)","rgb(150,150,150)","rgb(82,82,82)"],5:["rgb(247,247,247)","rgb(204,204,204)","rgb(150,150,150)","rgb(99,99,99)","rgb(37,37,37)"],6:["rgb(247,247,247)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(99,99,99)","rgb(37,37,37)"],7:["rgb(247,247,247)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)"],8:["rgb(255,255,255)","rgb(240,240,240)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)"],9:["rgb(255,255,255)","rgb(240,240,240)","rgb(217,217,217)","rgb(189,189,189)","rgb(150,150,150)","rgb(115,115,115)","rgb(82,82,82)","rgb(37,37,37)","rgb(0,0,0)"]}, 24 | PuOr:{3:["rgb(241,163,64)","rgb(247,247,247)","rgb(153,142,195)"],4:["rgb(230,97,1)","rgb(253,184,99)","rgb(178,171,210)","rgb(94,60,153)"],5:["rgb(230,97,1)","rgb(253,184,99)","rgb(247,247,247)","rgb(178,171,210)","rgb(94,60,153)"],6:["rgb(179,88,6)","rgb(241,163,64)","rgb(254,224,182)","rgb(216,218,235)","rgb(153,142,195)","rgb(84,39,136)"],7:["rgb(179,88,6)","rgb(241,163,64)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(153,142,195)","rgb(84,39,136)"],8:["rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)"],9:["rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)"],10:["rgb(127,59,8)","rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)","rgb(45,0,75)"],11:["rgb(127,59,8)","rgb(179,88,6)","rgb(224,130,20)","rgb(253,184,99)","rgb(254,224,182)","rgb(247,247,247)","rgb(216,218,235)","rgb(178,171,210)","rgb(128,115,172)","rgb(84,39,136)","rgb(45,0,75)"]}, 25 | BrBG:{3:["rgb(216,179,101)","rgb(245,245,245)","rgb(90,180,172)"],4:["rgb(166,97,26)","rgb(223,194,125)","rgb(128,205,193)","rgb(1,133,113)"],5:["rgb(166,97,26)","rgb(223,194,125)","rgb(245,245,245)","rgb(128,205,193)","rgb(1,133,113)"],6:["rgb(140,81,10)","rgb(216,179,101)","rgb(246,232,195)","rgb(199,234,229)","rgb(90,180,172)","rgb(1,102,94)"],7:["rgb(140,81,10)","rgb(216,179,101)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(90,180,172)","rgb(1,102,94)"],8:["rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)"],9:["rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)"],10:["rgb(84,48,5)","rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)","rgb(0,60,48)"],11:["rgb(84,48,5)","rgb(140,81,10)","rgb(191,129,45)","rgb(223,194,125)","rgb(246,232,195)","rgb(245,245,245)","rgb(199,234,229)","rgb(128,205,193)","rgb(53,151,143)","rgb(1,102,94)","rgb(0,60,48)"]}, 26 | PRGn:{3:["rgb(175,141,195)","rgb(247,247,247)","rgb(127,191,123)"],4:["rgb(123,50,148)","rgb(194,165,207)","rgb(166,219,160)","rgb(0,136,55)"],5:["rgb(123,50,148)","rgb(194,165,207)","rgb(247,247,247)","rgb(166,219,160)","rgb(0,136,55)"],6:["rgb(118,42,131)","rgb(175,141,195)","rgb(231,212,232)","rgb(217,240,211)","rgb(127,191,123)","rgb(27,120,55)"],7:["rgb(118,42,131)","rgb(175,141,195)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(127,191,123)","rgb(27,120,55)"],8:["rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)"],9:["rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)"],10:["rgb(64,0,75)","rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)","rgb(0,68,27)"],11:["rgb(64,0,75)","rgb(118,42,131)","rgb(153,112,171)","rgb(194,165,207)","rgb(231,212,232)","rgb(247,247,247)","rgb(217,240,211)","rgb(166,219,160)","rgb(90,174,97)","rgb(27,120,55)","rgb(0,68,27)"]}, 27 | PiYG:{3:["rgb(233,163,201)","rgb(247,247,247)","rgb(161,215,106)"],4:["rgb(208,28,139)","rgb(241,182,218)","rgb(184,225,134)","rgb(77,172,38)"],5:["rgb(208,28,139)","rgb(241,182,218)","rgb(247,247,247)","rgb(184,225,134)","rgb(77,172,38)"],6:["rgb(197,27,125)","rgb(233,163,201)","rgb(253,224,239)","rgb(230,245,208)","rgb(161,215,106)","rgb(77,146,33)"],7:["rgb(197,27,125)","rgb(233,163,201)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(161,215,106)","rgb(77,146,33)"],8:["rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)"],9:["rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)"],10:["rgb(142,1,82)","rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)","rgb(39,100,25)"],11:["rgb(142,1,82)","rgb(197,27,125)","rgb(222,119,174)","rgb(241,182,218)","rgb(253,224,239)","rgb(247,247,247)","rgb(230,245,208)","rgb(184,225,134)","rgb(127,188,65)","rgb(77,146,33)","rgb(39,100,25)"]}, 28 | RdBu:{3:["rgb(239,138,98)","rgb(247,247,247)","rgb(103,169,207)"],4:["rgb(202,0,32)","rgb(244,165,130)","rgb(146,197,222)","rgb(5,113,176)"],5:["rgb(202,0,32)","rgb(244,165,130)","rgb(247,247,247)","rgb(146,197,222)","rgb(5,113,176)"],6:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(209,229,240)","rgb(103,169,207)","rgb(33,102,172)"],7:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(103,169,207)","rgb(33,102,172)"],8:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)"],9:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)"],10:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)","rgb(5,48,97)"],11:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(247,247,247)","rgb(209,229,240)","rgb(146,197,222)","rgb(67,147,195)","rgb(33,102,172)","rgb(5,48,97)"]}, 29 | RdGy:{3:["rgb(239,138,98)","rgb(255,255,255)","rgb(153,153,153)"],4:["rgb(202,0,32)","rgb(244,165,130)","rgb(186,186,186)","rgb(64,64,64)"],5:["rgb(202,0,32)","rgb(244,165,130)","rgb(255,255,255)","rgb(186,186,186)","rgb(64,64,64)"],6:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(224,224,224)","rgb(153,153,153)","rgb(77,77,77)"],7:["rgb(178,24,43)","rgb(239,138,98)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(153,153,153)","rgb(77,77,77)"],8:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)"],9:["rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)"],10:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)","rgb(26,26,26)"],11:["rgb(103,0,31)","rgb(178,24,43)","rgb(214,96,77)","rgb(244,165,130)","rgb(253,219,199)","rgb(255,255,255)","rgb(224,224,224)","rgb(186,186,186)","rgb(135,135,135)","rgb(77,77,77)","rgb(26,26,26)"]}, 30 | RdYlBu:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(145,191,219)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(171,217,233)","rgb(44,123,182)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(171,217,233)","rgb(44,123,182)"],6:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,144)","rgb(224,243,248)","rgb(145,191,219)","rgb(69,117,180)"],7:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(145,191,219)","rgb(69,117,180)"],8:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)"],9:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)"],10:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)","rgb(49,54,149)"],11:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,144)","rgb(255,255,191)","rgb(224,243,248)","rgb(171,217,233)","rgb(116,173,209)","rgb(69,117,180)","rgb(49,54,149)"]}, 31 | Spectral:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(153,213,148)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(171,221,164)","rgb(43,131,186)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(171,221,164)","rgb(43,131,186)"],6:["rgb(213,62,79)","rgb(252,141,89)","rgb(254,224,139)","rgb(230,245,152)","rgb(153,213,148)","rgb(50,136,189)"],7:["rgb(213,62,79)","rgb(252,141,89)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(153,213,148)","rgb(50,136,189)"],8:["rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)"],9:["rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)"],10:["rgb(158,1,66)","rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)","rgb(94,79,162)"],11:["rgb(158,1,66)","rgb(213,62,79)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(230,245,152)","rgb(171,221,164)","rgb(102,194,165)","rgb(50,136,189)","rgb(94,79,162)"]}, 32 | RdYlGn:{3:["rgb(252,141,89)","rgb(255,255,191)","rgb(145,207,96)"],4:["rgb(215,25,28)","rgb(253,174,97)","rgb(166,217,106)","rgb(26,150,65)"],5:["rgb(215,25,28)","rgb(253,174,97)","rgb(255,255,191)","rgb(166,217,106)","rgb(26,150,65)"],6:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,139)","rgb(217,239,139)","rgb(145,207,96)","rgb(26,152,80)"],7:["rgb(215,48,39)","rgb(252,141,89)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(145,207,96)","rgb(26,152,80)"],8:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)"],9:["rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)"],10:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)","rgb(0,104,55)"],11:["rgb(165,0,38)","rgb(215,48,39)","rgb(244,109,67)","rgb(253,174,97)","rgb(254,224,139)","rgb(255,255,191)","rgb(217,239,139)","rgb(166,217,106)","rgb(102,189,99)","rgb(26,152,80)","rgb(0,104,55)"]}}; 33 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/d3.geom.js: -------------------------------------------------------------------------------- 1 | (function(){d3.geom = {}; 2 | /** 3 | * Computes a contour for a given input grid function using the marching 5 | * squares algorithm. Returns the contour polygon as an array of points. 6 | * 7 | * @param grid a two-input function(x, y) that returns true for values 8 | * inside the contour and false for values outside the contour. 9 | * @param start an optional starting point [x, y] on the grid. 10 | * @returns polygon [[x1, y1], [x2, y2], …] 11 | */ 12 | d3.geom.contour = function(grid, start) { 13 | var s = start || d3_geom_contourStart(grid), // starting point 14 | c = [], // contour polygon 15 | x = s[0], // current x position 16 | y = s[1], // current y position 17 | dx = 0, // next x direction 18 | dy = 0, // next y direction 19 | pdx = NaN, // previous x direction 20 | pdy = NaN, // previous y direction 21 | i = 0; 22 | 23 | do { 24 | // determine marching squares index 25 | i = 0; 26 | if (grid(x-1, y-1)) i += 1; 27 | if (grid(x, y-1)) i += 2; 28 | if (grid(x-1, y )) i += 4; 29 | if (grid(x, y )) i += 8; 30 | 31 | // determine next direction 32 | if (i === 6) { 33 | dx = pdy === -1 ? -1 : 1; 34 | dy = 0; 35 | } else if (i === 9) { 36 | dx = 0; 37 | dy = pdx === 1 ? -1 : 1; 38 | } else { 39 | dx = d3_geom_contourDx[i]; 40 | dy = d3_geom_contourDy[i]; 41 | } 42 | 43 | // update contour polygon 44 | if (dx != pdx && dy != pdy) { 45 | c.push([x, y]); 46 | pdx = dx; 47 | pdy = dy; 48 | } 49 | 50 | x += dx; 51 | y += dy; 52 | } while (s[0] != x || s[1] != y); 53 | 54 | return c; 55 | }; 56 | 57 | // lookup tables for marching directions 58 | var d3_geom_contourDx = [1, 0, 1, 1,-1, 0,-1, 1,0, 0,0,0,-1, 0,-1,NaN], 59 | d3_geom_contourDy = [0,-1, 0, 0, 0,-1, 0, 0,1,-1,1,1, 0,-1, 0,NaN]; 60 | 61 | function d3_geom_contourStart(grid) { 62 | var x = 0, 63 | y = 0; 64 | 65 | // search for a starting point; begin at origin 66 | // and proceed along outward-expanding diagonals 67 | while (true) { 68 | if (grid(x,y)) { 69 | return [x,y]; 70 | } 71 | if (x === 0) { 72 | x = y + 1; 73 | y = 0; 74 | } else { 75 | x = x - 1; 76 | y = y + 1; 77 | } 78 | } 79 | } 80 | /** 81 | * Computes the 2D convex hull of a set of points using Graham's scanning 82 | * algorithm. The algorithm has been implemented as described in Cormen, 83 | * Leiserson, and Rivest's Introduction to Algorithms. The running time of 84 | * this algorithm is O(n log n), where n is the number of input points. 85 | * 86 | * @param vertices [[x1, y1], [x2, y2], …] 87 | * @returns polygon [[x1, y1], [x2, y2], …] 88 | */ 89 | d3.geom.hull = function(vertices) { 90 | if (vertices.length < 3) return []; 91 | 92 | var len = vertices.length, 93 | plen = len - 1, 94 | points = [], 95 | stack = [], 96 | i, j, h = 0, x1, y1, x2, y2, u, v, a, sp; 97 | 98 | // find the starting ref point: leftmost point with the minimum y coord 99 | for (i=1; i= (x2*x2 + y2*y2)) { 129 | points[i].index = -1; 130 | } else { 131 | points[u].index = -1; 132 | a = points[i].angle; 133 | u = i; 134 | v = j; 135 | } 136 | } else { 137 | a = points[i].angle; 138 | u = i; 139 | v = j; 140 | } 141 | } 142 | 143 | // initialize the stack 144 | stack.push(h); 145 | for (i=0, j=0; i<2; ++j) { 146 | if (points[j].index !== -1) { 147 | stack.push(points[j].index); 148 | i++; 149 | } 150 | } 151 | sp = stack.length; 152 | 153 | // do graham's scan 154 | for (; j 0; 177 | } 178 | // Note: requires coordinates to be counterclockwise and convex! 179 | d3.geom.polygon = function(coordinates) { 180 | 181 | coordinates.area = function() { 182 | var i = 0, 183 | n = coordinates.length, 184 | a = coordinates[n - 1][0] * coordinates[0][1], 185 | b = coordinates[n - 1][1] * coordinates[0][0]; 186 | while (++i < n) { 187 | a += coordinates[i - 1][0] * coordinates[i][1]; 188 | b += coordinates[i - 1][1] * coordinates[i][0]; 189 | } 190 | return (b - a) * .5; 191 | }; 192 | 193 | coordinates.centroid = function(k) { 194 | var i = -1, 195 | n = coordinates.length, 196 | x = 0, 197 | y = 0, 198 | a, 199 | b = coordinates[n - 1], 200 | c; 201 | if (!arguments.length) k = -1 / (6 * coordinates.area()); 202 | while (++i < n) { 203 | a = b; 204 | b = coordinates[i]; 205 | c = a[0] * b[1] - b[0] * a[1]; 206 | x += (a[0] + b[0]) * c; 207 | y += (a[1] + b[1]) * c; 208 | } 209 | return [x * k, y * k]; 210 | }; 211 | 212 | // The Sutherland-Hodgman clipping algorithm. 213 | coordinates.clip = function(subject) { 214 | var input, 215 | i = -1, 216 | n = coordinates.length, 217 | j, 218 | m, 219 | a = coordinates[n - 1], 220 | b, 221 | c, 222 | d; 223 | while (++i < n) { 224 | input = subject.slice(); 225 | subject.length = 0; 226 | b = coordinates[i]; 227 | c = input[(m = input.length) - 1]; 228 | j = -1; 229 | while (++j < m) { 230 | d = input[j]; 231 | if (d3_geom_polygonInside(d, a, b)) { 232 | if (!d3_geom_polygonInside(c, a, b)) { 233 | subject.push(d3_geom_polygonIntersect(c, d, a, b)); 234 | } 235 | subject.push(d); 236 | } else if (d3_geom_polygonInside(c, a, b)) { 237 | subject.push(d3_geom_polygonIntersect(c, d, a, b)); 238 | } 239 | c = d; 240 | } 241 | a = b; 242 | } 243 | return subject; 244 | }; 245 | 246 | return coordinates; 247 | }; 248 | 249 | function d3_geom_polygonInside(p, a, b) { 250 | return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); 251 | } 252 | 253 | // Intersect two infinite lines cd and ab. 254 | function d3_geom_polygonIntersect(c, d, a, b) { 255 | var x1 = c[0], x2 = d[0], x3 = a[0], x4 = b[0], 256 | y1 = c[1], y2 = d[1], y3 = a[1], y4 = b[1], 257 | x13 = x1 - x3, 258 | x21 = x2 - x1, 259 | x43 = x4 - x3, 260 | y13 = y1 - y3, 261 | y21 = y2 - y1, 262 | y43 = y4 - y3, 263 | ua = (x43 * y13 - y43 * x13) / (y43 * x21 - x43 * y21); 264 | return [x1 + ua * x21, y1 + ua * y21]; 265 | } 266 | // Adapted from Nicolas Garcia Belmonte's JIT implementation: 267 | // http://blog.thejit.org/2010/02/12/voronoi-tessellation/ 268 | // http://blog.thejit.org/assets/voronoijs/voronoi.js 269 | // See lib/jit/LICENSE for details. 270 | 271 | // Notes: 272 | // 273 | // This implementation does not clip the returned polygons, so if you want to 274 | // clip them to a particular shape you will need to do that either in SVG or by 275 | // post-processing with d3.geom.polygon's clip method. 276 | // 277 | // If any vertices are coincident or have NaN positions, the behavior of this 278 | // method is undefined. Most likely invalid polygons will be returned. You 279 | // should filter invalid points, and consolidate coincident points, before 280 | // computing the tessellation. 281 | 282 | /** 283 | * @param vertices [[x1, y1], [x2, y2], …] 284 | * @returns polygons [[[x1, y1], [x2, y2], …], …] 285 | */ 286 | d3.geom.voronoi = function(vertices) { 287 | var polygons = vertices.map(function() { return []; }); 288 | 289 | d3_voronoi_tessellate(vertices, function(e) { 290 | var s1, 291 | s2, 292 | x1, 293 | x2, 294 | y1, 295 | y2; 296 | if (e.a === 1 && e.b >= 0) { 297 | s1 = e.ep.r; 298 | s2 = e.ep.l; 299 | } else { 300 | s1 = e.ep.l; 301 | s2 = e.ep.r; 302 | } 303 | if (e.a === 1) { 304 | y1 = s1 ? s1.y : -1e6; 305 | x1 = e.c - e.b * y1; 306 | y2 = s2 ? s2.y : 1e6; 307 | x2 = e.c - e.b * y2; 308 | } else { 309 | x1 = s1 ? s1.x : -1e6; 310 | y1 = e.c - e.a * x1; 311 | x2 = s2 ? s2.x : 1e6; 312 | y2 = e.c - e.a * x2; 313 | } 314 | var v1 = [x1, y1], 315 | v2 = [x2, y2]; 316 | polygons[e.region.l.index].push(v1, v2); 317 | polygons[e.region.r.index].push(v1, v2); 318 | }); 319 | 320 | // Reconnect the polygon segments into counterclockwise loops. 321 | return polygons.map(function(polygon, i) { 322 | var cx = vertices[i][0], 323 | cy = vertices[i][1]; 324 | polygon.forEach(function(v) { 325 | v.angle = Math.atan2(v[0] - cx, v[1] - cy); 326 | }); 327 | return polygon.sort(function(a, b) { 328 | return a.angle - b.angle; 329 | }).filter(function(d, i) { 330 | return !i || (d.angle - polygon[i - 1].angle > 1e-10); 331 | }); 332 | }); 333 | }; 334 | 335 | var d3_voronoi_opposite = {"l": "r", "r": "l"}; 336 | 337 | function d3_voronoi_tessellate(vertices, callback) { 338 | 339 | var Sites = { 340 | list: vertices 341 | .map(function(v, i) { 342 | return { 343 | index: i, 344 | x: v[0], 345 | y: v[1] 346 | }; 347 | }) 348 | .sort(function(a, b) { 349 | return a.y < b.y ? -1 350 | : a.y > b.y ? 1 351 | : a.x < b.x ? -1 352 | : a.x > b.x ? 1 353 | : 0; 354 | }), 355 | bottomSite: null 356 | }; 357 | 358 | var EdgeList = { 359 | list: [], 360 | leftEnd: null, 361 | rightEnd: null, 362 | 363 | init: function() { 364 | EdgeList.leftEnd = EdgeList.createHalfEdge(null, "l"); 365 | EdgeList.rightEnd = EdgeList.createHalfEdge(null, "l"); 366 | EdgeList.leftEnd.r = EdgeList.rightEnd; 367 | EdgeList.rightEnd.l = EdgeList.leftEnd; 368 | EdgeList.list.unshift(EdgeList.leftEnd, EdgeList.rightEnd); 369 | }, 370 | 371 | createHalfEdge: function(edge, side) { 372 | return { 373 | edge: edge, 374 | side: side, 375 | vertex: null, 376 | "l": null, 377 | "r": null 378 | }; 379 | }, 380 | 381 | insert: function(lb, he) { 382 | he.l = lb; 383 | he.r = lb.r; 384 | lb.r.l = he; 385 | lb.r = he; 386 | }, 387 | 388 | leftBound: function(p) { 389 | var he = EdgeList.leftEnd; 390 | do { 391 | he = he.r; 392 | } while (he != EdgeList.rightEnd && Geom.rightOf(he, p)); 393 | he = he.l; 394 | return he; 395 | }, 396 | 397 | del: function(he) { 398 | he.l.r = he.r; 399 | he.r.l = he.l; 400 | he.edge = null; 401 | }, 402 | 403 | right: function(he) { 404 | return he.r; 405 | }, 406 | 407 | left: function(he) { 408 | return he.l; 409 | }, 410 | 411 | leftRegion: function(he) { 412 | return he.edge == null 413 | ? Sites.bottomSite 414 | : he.edge.region[he.side]; 415 | }, 416 | 417 | rightRegion: function(he) { 418 | return he.edge == null 419 | ? Sites.bottomSite 420 | : he.edge.region[d3_voronoi_opposite[he.side]]; 421 | } 422 | }; 423 | 424 | var Geom = { 425 | 426 | bisect: function(s1, s2) { 427 | var newEdge = { 428 | region: {"l": s1, "r": s2}, 429 | ep: {"l": null, "r": null} 430 | }; 431 | 432 | var dx = s2.x - s1.x, 433 | dy = s2.y - s1.y, 434 | adx = dx > 0 ? dx : -dx, 435 | ady = dy > 0 ? dy : -dy; 436 | 437 | newEdge.c = s1.x * dx + s1.y * dy 438 | + (dx * dx + dy * dy) * .5; 439 | 440 | if (adx > ady) { 441 | newEdge.a = 1; 442 | newEdge.b = dy / dx; 443 | newEdge.c /= dx; 444 | } else { 445 | newEdge.b = 1; 446 | newEdge.a = dx / dy; 447 | newEdge.c /= dy; 448 | } 449 | 450 | return newEdge; 451 | }, 452 | 453 | intersect: function(el1, el2) { 454 | var e1 = el1.edge, 455 | e2 = el2.edge; 456 | if (!e1 || !e2 || (e1.region.r == e2.region.r)) { 457 | return null; 458 | } 459 | var d = (e1.a * e2.b) - (e1.b * e2.a); 460 | if (Math.abs(d) < 1e-10) { 461 | return null; 462 | } 463 | var xint = (e1.c * e2.b - e2.c * e1.b) / d, 464 | yint = (e2.c * e1.a - e1.c * e2.a) / d, 465 | e1r = e1.region.r, 466 | e2r = e2.region.r, 467 | el, 468 | e; 469 | if ((e1r.y < e2r.y) || 470 | (e1r.y == e2r.y && e1r.x < e2r.x)) { 471 | el = el1; 472 | e = e1; 473 | } else { 474 | el = el2; 475 | e = e2; 476 | } 477 | var rightOfSite = (xint >= e.region.r.x); 478 | if ((rightOfSite && (el.side === "l")) || 479 | (!rightOfSite && (el.side === "r"))) { 480 | return null; 481 | } 482 | return { 483 | x: xint, 484 | y: yint 485 | }; 486 | }, 487 | 488 | rightOf: function(he, p) { 489 | var e = he.edge, 490 | topsite = e.region.r, 491 | rightOfSite = (p.x > topsite.x); 492 | 493 | if (rightOfSite && (he.side === "l")) { 494 | return 1; 495 | } 496 | if (!rightOfSite && (he.side === "r")) { 497 | return 0; 498 | } 499 | if (e.a === 1) { 500 | var dyp = p.y - topsite.y, 501 | dxp = p.x - topsite.x, 502 | fast = 0, 503 | above = 0; 504 | 505 | if ((!rightOfSite && (e.b < 0)) || 506 | (rightOfSite && (e.b >= 0))) { 507 | above = fast = (dyp >= e.b * dxp); 508 | } else { 509 | above = ((p.x + p.y * e.b) > e.c); 510 | if (e.b < 0) { 511 | above = !above; 512 | } 513 | if (!above) { 514 | fast = 1; 515 | } 516 | } 517 | if (!fast) { 518 | var dxs = topsite.x - e.region.l.x; 519 | above = (e.b * (dxp * dxp - dyp * dyp)) < 520 | (dxs * dyp * (1 + 2 * dxp / dxs + e.b * e.b)); 521 | 522 | if (e.b < 0) { 523 | above = !above; 524 | } 525 | } 526 | } else /* e.b == 1 */ { 527 | var yl = e.c - e.a * p.x, 528 | t1 = p.y - yl, 529 | t2 = p.x - topsite.x, 530 | t3 = yl - topsite.y; 531 | 532 | above = (t1 * t1) > (t2 * t2 + t3 * t3); 533 | } 534 | return he.side === "l" ? above : !above; 535 | }, 536 | 537 | endPoint: function(edge, side, site) { 538 | edge.ep[side] = site; 539 | if (!edge.ep[d3_voronoi_opposite[side]]) return; 540 | callback(edge); 541 | }, 542 | 543 | distance: function(s, t) { 544 | var dx = s.x - t.x, 545 | dy = s.y - t.y; 546 | return Math.sqrt(dx * dx + dy * dy); 547 | } 548 | }; 549 | 550 | var EventQueue = { 551 | list: [], 552 | 553 | insert: function(he, site, offset) { 554 | he.vertex = site; 555 | he.ystar = site.y + offset; 556 | for (var i=0, list=EventQueue.list, l=list.length; i next.ystar || 559 | (he.ystar == next.ystar && 560 | site.x > next.vertex.x)) { 561 | continue; 562 | } else { 563 | break; 564 | } 565 | } 566 | list.splice(i, 0, he); 567 | }, 568 | 569 | del: function(he) { 570 | for (var i=0, ls=EventQueue.list, l=ls.length; i top.y) { 646 | temp = bot; 647 | bot = top; 648 | top = temp; 649 | pm = "r"; 650 | } 651 | e = Geom.bisect(bot, top); 652 | bisector = EdgeList.createHalfEdge(e, pm); 653 | EdgeList.insert(llbnd, bisector); 654 | Geom.endPoint(e, d3_voronoi_opposite[pm], v); 655 | p = Geom.intersect(llbnd, bisector); 656 | if (p) { 657 | EventQueue.del(llbnd); 658 | EventQueue.insert(llbnd, p, Geom.distance(p, bot)); 659 | } 660 | p = Geom.intersect(bisector, rrbnd); 661 | if (p) { 662 | EventQueue.insert(bisector, p, Geom.distance(p, bot)); 663 | } 664 | } else { 665 | break; 666 | } 667 | }//end while 668 | 669 | for (lbnd = EdgeList.right(EdgeList.leftEnd); 670 | lbnd != EdgeList.rightEnd; 671 | lbnd = EdgeList.right(lbnd)) { 672 | callback(lbnd.edge); 673 | } 674 | } 675 | /** 676 | * @param vertices [[x1, y1], [x2, y2], …] 677 | * @returns triangles [[[x1, y1], [x2, y2], [x3, y3]], …] 678 | */ 679 | d3.geom.delaunay = function(vertices) { 680 | var edges = vertices.map(function() { return []; }), 681 | triangles = []; 682 | 683 | // Use the Voronoi tessellation to determine Delaunay edges. 684 | d3_voronoi_tessellate(vertices, function(e) { 685 | edges[e.region.l.index].push(vertices[e.region.r.index]); 686 | }); 687 | 688 | // Reconnect the edges into counterclockwise triangles. 689 | edges.forEach(function(edge, i) { 690 | var v = vertices[i], 691 | cx = v[0], 692 | cy = v[1]; 693 | edge.forEach(function(v) { 694 | v.angle = Math.atan2(v[0] - cx, v[1] - cy); 695 | }); 696 | edge.sort(function(a, b) { 697 | return a.angle - b.angle; 698 | }); 699 | for (var j = 0, m = edge.length - 1; j < m; j++) { 700 | triangles.push([v, edge[j], edge[j + 1]]); 701 | } 702 | }); 703 | 704 | return triangles; 705 | }; 706 | // Constructs a new quadtree for the specified array of points. A quadtree is a 707 | // two-dimensional recursive spatial subdivision. This implementation uses 708 | // square partitions, dividing each square into four equally-sized squares. Each 709 | // point exists in a unique node; if multiple points are in the same position, 710 | // some points may be stored on internal nodes rather than leaf nodes. Quadtrees 711 | // can be used to accelerate various spatial operations, such as the Barnes-Hut 712 | // approximation for computing n-body forces, or collision detection. 713 | d3.geom.quadtree = function(points, x1, y1, x2, y2) { 714 | var p, 715 | i = -1, 716 | n = points.length; 717 | 718 | // Type conversion for deprecated API. 719 | if (n && isNaN(points[0].x)) points = points.map(d3_geom_quadtreePoint); 720 | 721 | // Allow bounds to be specified explicitly. 722 | if (arguments.length < 5) { 723 | if (arguments.length === 3) { 724 | y2 = x2 = y1; 725 | y1 = x1; 726 | } else { 727 | x1 = y1 = Infinity; 728 | x2 = y2 = -Infinity; 729 | 730 | // Compute bounds. 731 | while (++i < n) { 732 | p = points[i]; 733 | if (p.x < x1) x1 = p.x; 734 | if (p.y < y1) y1 = p.y; 735 | if (p.x > x2) x2 = p.x; 736 | if (p.y > y2) y2 = p.y; 737 | } 738 | 739 | // Squarify the bounds. 740 | var dx = x2 - x1, 741 | dy = y2 - y1; 742 | if (dx > dy) y2 = y1 + dx; 743 | else x2 = x1 + dy; 744 | } 745 | } 746 | 747 | // Recursively inserts the specified point p at the node n or one of its 748 | // descendants. The bounds are defined by [x1, x2] and [y1, y2]. 749 | function insert(n, p, x1, y1, x2, y2) { 750 | if (isNaN(p.x) || isNaN(p.y)) return; // ignore invalid points 751 | if (n.leaf) { 752 | var v = n.point; 753 | if (v) { 754 | // If the point at this leaf node is at the same position as the new 755 | // point we are adding, we leave the point associated with the 756 | // internal node while adding the new point to a child node. This 757 | // avoids infinite recursion. 758 | if ((Math.abs(v.x - p.x) + Math.abs(v.y - p.y)) < .01) { 759 | insertChild(n, p, x1, y1, x2, y2); 760 | } else { 761 | n.point = null; 762 | insertChild(n, v, x1, y1, x2, y2); 763 | insertChild(n, p, x1, y1, x2, y2); 764 | } 765 | } else { 766 | n.point = p; 767 | } 768 | } else { 769 | insertChild(n, p, x1, y1, x2, y2); 770 | } 771 | } 772 | 773 | // Recursively inserts the specified point p into a descendant of node n. The 774 | // bounds are defined by [x1, x2] and [y1, y2]. 775 | function insertChild(n, p, x1, y1, x2, y2) { 776 | // Compute the split point, and the quadrant in which to insert p. 777 | var sx = (x1 + x2) * .5, 778 | sy = (y1 + y2) * .5, 779 | right = p.x >= sx, 780 | bottom = p.y >= sy, 781 | i = (bottom << 1) + right; 782 | 783 | // Recursively insert into the child node. 784 | n.leaf = false; 785 | n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); 786 | 787 | // Update the bounds as we recurse. 788 | if (right) x1 = sx; else x2 = sx; 789 | if (bottom) y1 = sy; else y2 = sy; 790 | insert(n, p, x1, y1, x2, y2); 791 | } 792 | 793 | // Create the root node. 794 | var root = d3_geom_quadtreeNode(); 795 | 796 | root.add = function(p) { 797 | insert(root, p, x1, y1, x2, y2); 798 | }; 799 | 800 | root.visit = function(f) { 801 | d3_geom_quadtreeVisit(f, root, x1, y1, x2, y2); 802 | }; 803 | 804 | // Insert all points. 805 | points.forEach(root.add); 806 | return root; 807 | }; 808 | 809 | function d3_geom_quadtreeNode() { 810 | return { 811 | leaf: true, 812 | nodes: [], 813 | point: null 814 | }; 815 | } 816 | 817 | function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { 818 | if (!f(node, x1, y1, x2, y2)) { 819 | var sx = (x1 + x2) * .5, 820 | sy = (y1 + y2) * .5, 821 | children = node.nodes; 822 | if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); 823 | if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); 824 | if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); 825 | if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); 826 | } 827 | } 828 | 829 | function d3_geom_quadtreePoint(p) { 830 | return { 831 | x: p[0], 832 | y: p[1] 833 | }; 834 | } 835 | })(); 836 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/d3.geo.js: -------------------------------------------------------------------------------- 1 | (function(){d3.geo = {}; 2 | 3 | var d3_geo_radians = Math.PI / 180; 4 | // TODO clip input coordinates on opposite hemisphere 5 | d3.geo.azimuthal = function() { 6 | var mode = "orthographic", // or stereographic, gnomonic, equidistant or equalarea 7 | origin, 8 | scale = 200, 9 | translate = [480, 250], 10 | x0, 11 | y0, 12 | cy0, 13 | sy0; 14 | 15 | function azimuthal(coordinates) { 16 | var x1 = coordinates[0] * d3_geo_radians - x0, 17 | y1 = coordinates[1] * d3_geo_radians, 18 | cx1 = Math.cos(x1), 19 | sx1 = Math.sin(x1), 20 | cy1 = Math.cos(y1), 21 | sy1 = Math.sin(y1), 22 | cc = mode !== "orthographic" ? sy0 * sy1 + cy0 * cy1 * cx1 : null, 23 | c, 24 | k = mode === "stereographic" ? 1 / (1 + cc) 25 | : mode === "gnomonic" ? 1 / cc 26 | : mode === "equidistant" ? (c = Math.acos(cc), c ? c / Math.sin(c) : 0) 27 | : mode === "equalarea" ? Math.sqrt(2 / (1 + cc)) 28 | : 1, 29 | x = k * cy1 * sx1, 30 | y = k * (sy0 * cy1 * cx1 - cy0 * sy1); 31 | return [ 32 | scale * x + translate[0], 33 | scale * y + translate[1] 34 | ]; 35 | } 36 | 37 | azimuthal.invert = function(coordinates) { 38 | var x = (coordinates[0] - translate[0]) / scale, 39 | y = (coordinates[1] - translate[1]) / scale, 40 | p = Math.sqrt(x * x + y * y), 41 | c = mode === "stereographic" ? 2 * Math.atan(p) 42 | : mode === "gnomonic" ? Math.atan(p) 43 | : mode === "equidistant" ? p 44 | : mode === "equalarea" ? 2 * Math.asin(.5 * p) 45 | : Math.asin(p), 46 | sc = Math.sin(c), 47 | cc = Math.cos(c); 48 | return [ 49 | (x0 + Math.atan2(x * sc, p * cy0 * cc + y * sy0 * sc)) / d3_geo_radians, 50 | Math.asin(cc * sy0 - (p ? (y * sc * cy0) / p : 0)) / d3_geo_radians 51 | ]; 52 | }; 53 | 54 | azimuthal.mode = function(x) { 55 | if (!arguments.length) return mode; 56 | mode = x + ""; 57 | return azimuthal; 58 | }; 59 | 60 | azimuthal.origin = function(x) { 61 | if (!arguments.length) return origin; 62 | origin = x; 63 | x0 = origin[0] * d3_geo_radians; 64 | y0 = origin[1] * d3_geo_radians; 65 | cy0 = Math.cos(y0); 66 | sy0 = Math.sin(y0); 67 | return azimuthal; 68 | }; 69 | 70 | azimuthal.scale = function(x) { 71 | if (!arguments.length) return scale; 72 | scale = +x; 73 | return azimuthal; 74 | }; 75 | 76 | azimuthal.translate = function(x) { 77 | if (!arguments.length) return translate; 78 | translate = [+x[0], +x[1]]; 79 | return azimuthal; 80 | }; 81 | 82 | return azimuthal.origin([0, 0]); 83 | }; 84 | // Derived from Tom Carden's Albers implementation for Protovis. 85 | // http://gist.github.com/476238 86 | // http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html 87 | 88 | d3.geo.albers = function() { 89 | var origin = [-98, 38], 90 | parallels = [29.5, 45.5], 91 | scale = 1000, 92 | translate = [480, 250], 93 | lng0, // d3_geo_radians * origin[0] 94 | n, 95 | C, 96 | p0; 97 | 98 | function albers(coordinates) { 99 | var t = n * (d3_geo_radians * coordinates[0] - lng0), 100 | p = Math.sqrt(C - 2 * n * Math.sin(d3_geo_radians * coordinates[1])) / n; 101 | return [ 102 | scale * p * Math.sin(t) + translate[0], 103 | scale * (p * Math.cos(t) - p0) + translate[1] 104 | ]; 105 | } 106 | 107 | albers.invert = function(coordinates) { 108 | var x = (coordinates[0] - translate[0]) / scale, 109 | y = (coordinates[1] - translate[1]) / scale, 110 | p0y = p0 + y, 111 | t = Math.atan2(x, p0y), 112 | p = Math.sqrt(x * x + p0y * p0y); 113 | return [ 114 | (lng0 + t / n) / d3_geo_radians, 115 | Math.asin((C - p * p * n * n) / (2 * n)) / d3_geo_radians 116 | ]; 117 | }; 118 | 119 | function reload() { 120 | var phi1 = d3_geo_radians * parallels[0], 121 | phi2 = d3_geo_radians * parallels[1], 122 | lat0 = d3_geo_radians * origin[1], 123 | s = Math.sin(phi1), 124 | c = Math.cos(phi1); 125 | lng0 = d3_geo_radians * origin[0]; 126 | n = .5 * (s + Math.sin(phi2)); 127 | C = c * c + 2 * n * s; 128 | p0 = Math.sqrt(C - 2 * n * Math.sin(lat0)) / n; 129 | return albers; 130 | } 131 | 132 | albers.origin = function(x) { 133 | if (!arguments.length) return origin; 134 | origin = [+x[0], +x[1]]; 135 | return reload(); 136 | }; 137 | 138 | albers.parallels = function(x) { 139 | if (!arguments.length) return parallels; 140 | parallels = [+x[0], +x[1]]; 141 | return reload(); 142 | }; 143 | 144 | albers.scale = function(x) { 145 | if (!arguments.length) return scale; 146 | scale = +x; 147 | return albers; 148 | }; 149 | 150 | albers.translate = function(x) { 151 | if (!arguments.length) return translate; 152 | translate = [+x[0], +x[1]]; 153 | return albers; 154 | }; 155 | 156 | return reload(); 157 | }; 158 | 159 | // A composite projection for the United States, 960x500. The set of standard 160 | // parallels for each region comes from USGS, which is published here: 161 | // http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers 162 | // TODO allow the composite projection to be rescaled? 163 | d3.geo.albersUsa = function() { 164 | var lower48 = d3.geo.albers(); 165 | 166 | var alaska = d3.geo.albers() 167 | .origin([-160, 60]) 168 | .parallels([55, 65]); 169 | 170 | var hawaii = d3.geo.albers() 171 | .origin([-160, 20]) 172 | .parallels([8, 18]); 173 | 174 | var puertoRico = d3.geo.albers() 175 | .origin([-60, 10]) 176 | .parallels([8, 18]); 177 | 178 | function albersUsa(coordinates) { 179 | var lon = coordinates[0], 180 | lat = coordinates[1]; 181 | return (lat > 50 ? alaska 182 | : lon < -140 ? hawaii 183 | : lat < 21 ? puertoRico 184 | : lower48)(coordinates); 185 | } 186 | 187 | albersUsa.scale = function(x) { 188 | if (!arguments.length) return lower48.scale(); 189 | lower48.scale(x); 190 | alaska.scale(x * .6); 191 | hawaii.scale(x); 192 | puertoRico.scale(x * 1.5); 193 | return albersUsa.translate(lower48.translate()); 194 | }; 195 | 196 | albersUsa.translate = function(x) { 197 | if (!arguments.length) return lower48.translate(); 198 | var dz = lower48.scale() / 1000, 199 | dx = x[0], 200 | dy = x[1]; 201 | lower48.translate(x); 202 | alaska.translate([dx - 400 * dz, dy + 170 * dz]); 203 | hawaii.translate([dx - 190 * dz, dy + 200 * dz]); 204 | puertoRico.translate([dx + 580 * dz, dy + 430 * dz]); 205 | return albersUsa; 206 | }; 207 | 208 | return albersUsa.scale(lower48.scale()); 209 | }; 210 | d3.geo.bonne = function() { 211 | var scale = 200, 212 | translate = [480, 250], 213 | x0, // origin longitude in radians 214 | y0, // origin latitude in radians 215 | y1, // parallel latitude in radians 216 | c1; // cot(y1) 217 | 218 | function bonne(coordinates) { 219 | var x = coordinates[0] * d3_geo_radians - x0, 220 | y = coordinates[1] * d3_geo_radians - y0; 221 | if (y1) { 222 | var p = c1 + y1 - y, E = x * Math.cos(y) / p; 223 | x = p * Math.sin(E); 224 | y = p * Math.cos(E) - c1; 225 | } else { 226 | x *= Math.cos(y); 227 | y *= -1; 228 | } 229 | return [ 230 | scale * x + translate[0], 231 | scale * y + translate[1] 232 | ]; 233 | } 234 | 235 | bonne.invert = function(coordinates) { 236 | var x = (coordinates[0] - translate[0]) / scale, 237 | y = (coordinates[1] - translate[1]) / scale; 238 | if (y1) { 239 | var c = c1 + y, p = Math.sqrt(x * x + c * c); 240 | y = c1 + y1 - p; 241 | x = x0 + p * Math.atan2(x, c) / Math.cos(y); 242 | } else { 243 | y *= -1; 244 | x /= Math.cos(y); 245 | } 246 | return [ 247 | x / d3_geo_radians, 248 | y / d3_geo_radians 249 | ]; 250 | }; 251 | 252 | // 90° for Werner, 0° for Sinusoidal 253 | bonne.parallel = function(x) { 254 | if (!arguments.length) return y1 / d3_geo_radians; 255 | c1 = 1 / Math.tan(y1 = x * d3_geo_radians); 256 | return bonne; 257 | }; 258 | 259 | bonne.origin = function(x) { 260 | if (!arguments.length) return [x0 / d3_geo_radians, y0 / d3_geo_radians]; 261 | x0 = x[0] * d3_geo_radians; 262 | y0 = x[1] * d3_geo_radians; 263 | return bonne; 264 | }; 265 | 266 | bonne.scale = function(x) { 267 | if (!arguments.length) return scale; 268 | scale = +x; 269 | return bonne; 270 | }; 271 | 272 | bonne.translate = function(x) { 273 | if (!arguments.length) return translate; 274 | translate = [+x[0], +x[1]]; 275 | return bonne; 276 | }; 277 | 278 | return bonne.origin([0, 0]).parallel(45); 279 | }; 280 | d3.geo.equirectangular = function() { 281 | var scale = 500, 282 | translate = [480, 250]; 283 | 284 | function equirectangular(coordinates) { 285 | var x = coordinates[0] / 360, 286 | y = -coordinates[1] / 360; 287 | return [ 288 | scale * x + translate[0], 289 | scale * y + translate[1] 290 | ]; 291 | } 292 | 293 | equirectangular.invert = function(coordinates) { 294 | var x = (coordinates[0] - translate[0]) / scale, 295 | y = (coordinates[1] - translate[1]) / scale; 296 | return [ 297 | 360 * x, 298 | -360 * y 299 | ]; 300 | }; 301 | 302 | equirectangular.scale = function(x) { 303 | if (!arguments.length) return scale; 304 | scale = +x; 305 | return equirectangular; 306 | }; 307 | 308 | equirectangular.translate = function(x) { 309 | if (!arguments.length) return translate; 310 | translate = [+x[0], +x[1]]; 311 | return equirectangular; 312 | }; 313 | 314 | return equirectangular; 315 | }; 316 | d3.geo.mercator = function() { 317 | var scale = 500, 318 | translate = [480, 250]; 319 | 320 | function mercator(coordinates) { 321 | var x = coordinates[0] / 360, 322 | y = -(Math.log(Math.tan(Math.PI / 4 + coordinates[1] * d3_geo_radians / 2)) / d3_geo_radians) / 360; 323 | return [ 324 | scale * x + translate[0], 325 | scale * Math.max(-.5, Math.min(.5, y)) + translate[1] 326 | ]; 327 | } 328 | 329 | mercator.invert = function(coordinates) { 330 | var x = (coordinates[0] - translate[0]) / scale, 331 | y = (coordinates[1] - translate[1]) / scale; 332 | return [ 333 | 360 * x, 334 | 2 * Math.atan(Math.exp(-360 * y * d3_geo_radians)) / d3_geo_radians - 90 335 | ]; 336 | }; 337 | 338 | mercator.scale = function(x) { 339 | if (!arguments.length) return scale; 340 | scale = +x; 341 | return mercator; 342 | }; 343 | 344 | mercator.translate = function(x) { 345 | if (!arguments.length) return translate; 346 | translate = [+x[0], +x[1]]; 347 | return mercator; 348 | }; 349 | 350 | return mercator; 351 | }; 352 | function d3_geo_type(types, defaultValue) { 353 | return function(object) { 354 | return object && object.type in types ? types[object.type](object) : defaultValue; 355 | }; 356 | } 357 | /** 358 | * Returns a function that, given a GeoJSON object (e.g., a feature), returns 359 | * the corresponding SVG path. The function can be customized by overriding the 360 | * projection. Point features are mapped to circles with a default radius of 361 | * 4.5px; the radius can be specified either as a constant or a function that 362 | * is evaluated per object. 363 | */ 364 | d3.geo.path = function() { 365 | var pointRadius = 4.5, 366 | pointCircle = d3_path_circle(pointRadius), 367 | projection = d3.geo.albersUsa(); 368 | 369 | function path(d, i) { 370 | if (typeof pointRadius === "function") { 371 | pointCircle = d3_path_circle(pointRadius.apply(this, arguments)); 372 | } 373 | return pathType(d) || null; 374 | } 375 | 376 | function project(coordinates) { 377 | return projection(coordinates).join(","); 378 | } 379 | 380 | var pathType = d3_geo_type({ 381 | 382 | FeatureCollection: function(o) { 383 | var path = [], 384 | features = o.features, 385 | i = -1, // features.index 386 | n = features.length; 387 | while (++i < n) path.push(pathType(features[i].geometry)); 388 | return path.join(""); 389 | }, 390 | 391 | Feature: function(o) { 392 | return pathType(o.geometry); 393 | }, 394 | 395 | Point: function(o) { 396 | return "M" + project(o.coordinates) + pointCircle; 397 | }, 398 | 399 | MultiPoint: function(o) { 400 | var path = [], 401 | coordinates = o.coordinates, 402 | i = -1, // coordinates.index 403 | n = coordinates.length; 404 | while (++i < n) path.push("M", project(coordinates[i]), pointCircle); 405 | return path.join(""); 406 | }, 407 | 408 | LineString: function(o) { 409 | var path = ["M"], 410 | coordinates = o.coordinates, 411 | i = -1, // coordinates.index 412 | n = coordinates.length; 413 | while (++i < n) path.push(project(coordinates[i]), "L"); 414 | path.pop(); 415 | return path.join(""); 416 | }, 417 | 418 | MultiLineString: function(o) { 419 | var path = [], 420 | coordinates = o.coordinates, 421 | i = -1, // coordinates.index 422 | n = coordinates.length, 423 | subcoordinates, // coordinates[i] 424 | j, // subcoordinates.index 425 | m; // subcoordinates.length 426 | while (++i < n) { 427 | subcoordinates = coordinates[i]; 428 | j = -1; 429 | m = subcoordinates.length; 430 | path.push("M"); 431 | while (++j < m) path.push(project(subcoordinates[j]), "L"); 432 | path.pop(); 433 | } 434 | return path.join(""); 435 | }, 436 | 437 | Polygon: function(o) { 438 | var path = [], 439 | coordinates = o.coordinates, 440 | i = -1, // coordinates.index 441 | n = coordinates.length, 442 | subcoordinates, // coordinates[i] 443 | j, // subcoordinates.index 444 | m; // subcoordinates.length 445 | while (++i < n) { 446 | subcoordinates = coordinates[i]; 447 | j = -1; 448 | if ((m = subcoordinates.length - 1) > 0) { 449 | path.push("M"); 450 | while (++j < m) path.push(project(subcoordinates[j]), "L"); 451 | path[path.length - 1] = "Z"; 452 | } 453 | } 454 | return path.join(""); 455 | }, 456 | 457 | MultiPolygon: function(o) { 458 | var path = [], 459 | coordinates = o.coordinates, 460 | i = -1, // coordinates index 461 | n = coordinates.length, 462 | subcoordinates, // coordinates[i] 463 | j, // subcoordinates index 464 | m, // subcoordinates.length 465 | subsubcoordinates, // subcoordinates[j] 466 | k, // subsubcoordinates index 467 | p; // subsubcoordinates.length 468 | while (++i < n) { 469 | subcoordinates = coordinates[i]; 470 | j = -1; 471 | m = subcoordinates.length; 472 | while (++j < m) { 473 | subsubcoordinates = subcoordinates[j]; 474 | k = -1; 475 | if ((p = subsubcoordinates.length - 1) > 0) { 476 | path.push("M"); 477 | while (++k < p) path.push(project(subsubcoordinates[k]), "L"); 478 | path[path.length - 1] = "Z"; 479 | } 480 | } 481 | } 482 | return path.join(""); 483 | }, 484 | 485 | GeometryCollection: function(o) { 486 | var path = [], 487 | geometries = o.geometries, 488 | i = -1, // geometries index 489 | n = geometries.length; 490 | while (++i < n) path.push(pathType(geometries[i])); 491 | return path.join(""); 492 | } 493 | 494 | }); 495 | 496 | var areaType = path.area = d3_geo_type({ 497 | 498 | FeatureCollection: function(o) { 499 | var area = 0, 500 | features = o.features, 501 | i = -1, // features.index 502 | n = features.length; 503 | while (++i < n) area += areaType(features[i]); 504 | return area; 505 | }, 506 | 507 | Feature: function(o) { 508 | return areaType(o.geometry); 509 | }, 510 | 511 | Polygon: function(o) { 512 | return polygonArea(o.coordinates); 513 | }, 514 | 515 | MultiPolygon: function(o) { 516 | var sum = 0, 517 | coordinates = o.coordinates, 518 | i = -1, // coordinates index 519 | n = coordinates.length; 520 | while (++i < n) sum += polygonArea(coordinates[i]); 521 | return sum; 522 | }, 523 | 524 | GeometryCollection: function(o) { 525 | var sum = 0, 526 | geometries = o.geometries, 527 | i = -1, // geometries index 528 | n = geometries.length; 529 | while (++i < n) sum += areaType(geometries[i]); 530 | return sum; 531 | } 532 | 533 | }, 0); 534 | 535 | function polygonArea(coordinates) { 536 | var sum = area(coordinates[0]), // exterior ring 537 | i = 0, // coordinates.index 538 | n = coordinates.length; 539 | while (++i < n) sum -= area(coordinates[i]); // holes 540 | return sum; 541 | } 542 | 543 | function polygonCentroid(coordinates) { 544 | var polygon = d3.geom.polygon(coordinates[0].map(projection)), // exterior ring 545 | area = polygon.area(), 546 | centroid = polygon.centroid(area < 0 ? (area *= -1, 1) : -1), 547 | x = centroid[0], 548 | y = centroid[1], 549 | z = area, 550 | i = 0, // coordinates index 551 | n = coordinates.length; 552 | while (++i < n) { 553 | polygon = d3.geom.polygon(coordinates[i].map(projection)); // holes 554 | area = polygon.area(); 555 | centroid = polygon.centroid(area < 0 ? (area *= -1, 1) : -1); 556 | x -= centroid[0]; 557 | y -= centroid[1]; 558 | z -= area; 559 | } 560 | return [x, y, 6 * z]; // weighted centroid 561 | } 562 | 563 | var centroidType = path.centroid = d3_geo_type({ 564 | 565 | // TODO FeatureCollection 566 | // TODO Point 567 | // TODO MultiPoint 568 | // TODO LineString 569 | // TODO MultiLineString 570 | // TODO GeometryCollection 571 | 572 | Feature: function(o) { 573 | return centroidType(o.geometry); 574 | }, 575 | 576 | Polygon: function(o) { 577 | var centroid = polygonCentroid(o.coordinates); 578 | return [centroid[0] / centroid[2], centroid[1] / centroid[2]]; 579 | }, 580 | 581 | MultiPolygon: function(o) { 582 | var area = 0, 583 | coordinates = o.coordinates, 584 | centroid, 585 | x = 0, 586 | y = 0, 587 | z = 0, 588 | i = -1, // coordinates index 589 | n = coordinates.length; 590 | while (++i < n) { 591 | centroid = polygonCentroid(coordinates[i]); 592 | x += centroid[0]; 593 | y += centroid[1]; 594 | z += centroid[2]; 595 | } 596 | return [x / z, y / z]; 597 | } 598 | 599 | }); 600 | 601 | function area(coordinates) { 602 | return Math.abs(d3.geom.polygon(coordinates.map(projection)).area()); 603 | } 604 | 605 | path.projection = function(x) { 606 | projection = x; 607 | return path; 608 | }; 609 | 610 | path.pointRadius = function(x) { 611 | if (typeof x === "function") pointRadius = x; 612 | else { 613 | pointRadius = +x; 614 | pointCircle = d3_path_circle(pointRadius); 615 | } 616 | return path; 617 | }; 618 | 619 | return path; 620 | }; 621 | 622 | function d3_path_circle(radius) { 623 | return "m0," + radius 624 | + "a" + radius + "," + radius + " 0 1,1 0," + (-2 * radius) 625 | + "a" + radius + "," + radius + " 0 1,1 0," + (+2 * radius) 626 | + "z"; 627 | } 628 | /** 629 | * Given a GeoJSON object, returns the corresponding bounding box. The bounding 630 | * box is represented by a two-dimensional array: [[left, bottom], [right, 631 | * top]], where left is the minimum longitude, bottom is the minimum latitude, 632 | * right is maximum longitude, and top is the maximum latitude. 633 | */ 634 | d3.geo.bounds = function(feature) { 635 | var left = Infinity, 636 | bottom = Infinity, 637 | right = -Infinity, 638 | top = -Infinity; 639 | d3_geo_bounds(feature, function(x, y) { 640 | if (x < left) left = x; 641 | if (x > right) right = x; 642 | if (y < bottom) bottom = y; 643 | if (y > top) top = y; 644 | }); 645 | return [[left, bottom], [right, top]]; 646 | }; 647 | 648 | function d3_geo_bounds(o, f) { 649 | if (o.type in d3_geo_boundsTypes) d3_geo_boundsTypes[o.type](o, f); 650 | } 651 | 652 | var d3_geo_boundsTypes = { 653 | Feature: d3_geo_boundsFeature, 654 | FeatureCollection: d3_geo_boundsFeatureCollection, 655 | GeometryCollection: d3_geo_boundsGeometryCollection, 656 | LineString: d3_geo_boundsLineString, 657 | MultiLineString: d3_geo_boundsMultiLineString, 658 | MultiPoint: d3_geo_boundsLineString, 659 | MultiPolygon: d3_geo_boundsMultiPolygon, 660 | Point: d3_geo_boundsPoint, 661 | Polygon: d3_geo_boundsPolygon 662 | }; 663 | 664 | function d3_geo_boundsFeature(o, f) { 665 | d3_geo_bounds(o.geometry, f); 666 | } 667 | 668 | function d3_geo_boundsFeatureCollection(o, f) { 669 | for (var a = o.features, i = 0, n = a.length; i < n; i++) { 670 | d3_geo_bounds(a[i].geometry, f); 671 | } 672 | } 673 | 674 | function d3_geo_boundsGeometryCollection(o, f) { 675 | for (var a = o.geometries, i = 0, n = a.length; i < n; i++) { 676 | d3_geo_bounds(a[i], f); 677 | } 678 | } 679 | 680 | function d3_geo_boundsLineString(o, f) { 681 | for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) { 682 | f.apply(null, a[i]); 683 | } 684 | } 685 | 686 | function d3_geo_boundsMultiLineString(o, f) { 687 | for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) { 688 | for (var b = a[i], j = 0, m = b.length; j < m; j++) { 689 | f.apply(null, b[j]); 690 | } 691 | } 692 | } 693 | 694 | function d3_geo_boundsMultiPolygon(o, f) { 695 | for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) { 696 | for (var b = a[i][0], j = 0, m = b.length; j < m; j++) { 697 | f.apply(null, b[j]); 698 | } 699 | } 700 | } 701 | 702 | function d3_geo_boundsPoint(o, f) { 703 | f.apply(null, o.coordinates); 704 | } 705 | 706 | function d3_geo_boundsPolygon(o, f) { 707 | for (var a = o.coordinates[0], i = 0, n = a.length; i < n; i++) { 708 | f.apply(null, a[i]); 709 | } 710 | } 711 | // TODO breakAtDateLine? 712 | 713 | d3.geo.circle = function() { 714 | var origin = [0, 0], 715 | degrees = 90 - 1e-2, 716 | radians = degrees * d3_geo_radians, 717 | arc = d3.geo.greatArc().target(Object); 718 | 719 | function circle() { 720 | // TODO render a circle as a Polygon 721 | } 722 | 723 | function visible(point) { 724 | return arc.distance(point) < radians; 725 | } 726 | 727 | circle.clip = function(d) { 728 | arc.source(typeof origin === "function" ? origin.apply(this, arguments) : origin); 729 | return clipType(d); 730 | }; 731 | 732 | var clipType = d3_geo_type({ 733 | 734 | FeatureCollection: function(o) { 735 | var features = o.features.map(clipType).filter(Object); 736 | return features && (o = Object.create(o), o.features = features, o); 737 | }, 738 | 739 | Feature: function(o) { 740 | var geometry = clipType(o.geometry); 741 | return geometry && (o = Object.create(o), o.geometry = geometry, o); 742 | }, 743 | 744 | Point: function(o) { 745 | return visible(o.coordinates) && o; 746 | }, 747 | 748 | MultiPoint: function(o) { 749 | var coordinates = o.coordinates.filter(visible); 750 | return coordinates.length && { 751 | type: o.type, 752 | coordinates: coordinates 753 | }; 754 | }, 755 | 756 | LineString: function(o) { 757 | var coordinates = clip(o.coordinates); 758 | return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o); 759 | }, 760 | 761 | MultiLineString: function(o) { 762 | var coordinates = o.coordinates.map(clip).filter(function(d) { return d.length; }); 763 | return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o); 764 | }, 765 | 766 | Polygon: function(o) { 767 | var coordinates = o.coordinates.map(clip); 768 | return coordinates[0].length && (o = Object.create(o), o.coordinates = coordinates, o); 769 | }, 770 | 771 | MultiPolygon: function(o) { 772 | var coordinates = o.coordinates.map(function(d) { return d.map(clip); }).filter(function(d) { return d[0].length; }); 773 | return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o); 774 | }, 775 | 776 | GeometryCollection: function(o) { 777 | var geometries = o.geometries.map(clipType).filter(Object); 778 | return geometries.length && (o = Object.create(o), o.geometries = geometries, o); 779 | } 780 | 781 | }); 782 | 783 | function clip(coordinates) { 784 | var i = -1, 785 | n = coordinates.length, 786 | clipped = [], 787 | p0, 788 | p1, 789 | p2, 790 | d0, 791 | d1; 792 | 793 | while (++i < n) { 794 | d1 = arc.distance(p2 = coordinates[i]); 795 | if (d1 < radians) { 796 | if (p1) clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1))); 797 | clipped.push(p2); 798 | p0 = p1 = null; 799 | } else { 800 | p1 = p2; 801 | if (!p0 && clipped.length) { 802 | clipped.push(d3_geo_greatArcInterpolate(clipped[clipped.length - 1], p1)((radians - d0) / (d1 - d0))); 803 | p0 = p1; 804 | } 805 | } 806 | d0 = d1; 807 | } 808 | 809 | if (p1 && clipped.length) { 810 | d1 = arc.distance(p2 = clipped[0]); 811 | clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1))); 812 | } 813 | 814 | return resample(clipped); 815 | } 816 | 817 | // Resample coordinates, creating great arcs between each. 818 | function resample(coordinates) { 819 | var i = 0, 820 | n = coordinates.length, 821 | j, 822 | m, 823 | resampled = n ? [coordinates[0]] : coordinates, 824 | resamples, 825 | origin = arc.source(); 826 | 827 | while (++i < n) { 828 | resamples = arc.source(coordinates[i - 1])(coordinates[i]).coordinates; 829 | for (j = 0, m = resamples.length; ++j < m;) resampled.push(resamples[j]); 830 | } 831 | 832 | arc.source(origin); 833 | return resampled; 834 | } 835 | 836 | circle.origin = function(x) { 837 | if (!arguments.length) return origin; 838 | origin = x; 839 | return circle; 840 | }; 841 | 842 | circle.angle = function(x) { 843 | if (!arguments.length) return degrees; 844 | radians = (degrees = +x) * d3_geo_radians; 845 | return circle; 846 | }; 847 | 848 | // Precision is specified in degrees. 849 | circle.precision = function(x) { 850 | if (!arguments.length) return arc.precision(); 851 | arc.precision(x); 852 | return circle; 853 | }; 854 | 855 | return circle; 856 | } 857 | d3.geo.greatArc = function() { 858 | var source = d3_geo_greatArcSource, 859 | target = d3_geo_greatArcTarget, 860 | precision = 6 * d3_geo_radians; 861 | 862 | function greatArc() { 863 | var a = typeof source === "function" ? source.apply(this, arguments) : source, 864 | b = typeof target === "function" ? target.apply(this, arguments) : target, 865 | i = d3_geo_greatArcInterpolate(a, b), 866 | dt = precision / i.d, 867 | t = 0, 868 | coordinates = [a]; 869 | while ((t += dt) < 1) coordinates.push(i(t)); 870 | coordinates.push(b); 871 | return { 872 | type: "LineString", 873 | coordinates: coordinates 874 | }; 875 | } 876 | 877 | // Length returned in radians; multiply by radius for distance. 878 | greatArc.distance = function() { 879 | var a = typeof source === "function" ? source.apply(this, arguments) : source, 880 | b = typeof target === "function" ? target.apply(this, arguments) : target; 881 | return d3_geo_greatArcInterpolate(a, b).d; 882 | }; 883 | 884 | greatArc.source = function(x) { 885 | if (!arguments.length) return source; 886 | source = x; 887 | return greatArc; 888 | }; 889 | 890 | greatArc.target = function(x) { 891 | if (!arguments.length) return target; 892 | target = x; 893 | return greatArc; 894 | }; 895 | 896 | // Precision is specified in degrees. 897 | greatArc.precision = function(x) { 898 | if (!arguments.length) return precision / d3_geo_radians; 899 | precision = x * d3_geo_radians; 900 | return greatArc; 901 | }; 902 | 903 | return greatArc; 904 | }; 905 | 906 | function d3_geo_greatArcSource(d) { 907 | return d.source; 908 | } 909 | 910 | function d3_geo_greatArcTarget(d) { 911 | return d.target; 912 | } 913 | 914 | function d3_geo_greatArcInterpolate(a, b) { 915 | var x0 = a[0] * d3_geo_radians, cx0 = Math.cos(x0), sx0 = Math.sin(x0), 916 | y0 = a[1] * d3_geo_radians, cy0 = Math.cos(y0), sy0 = Math.sin(y0), 917 | x1 = b[0] * d3_geo_radians, cx1 = Math.cos(x1), sx1 = Math.sin(x1), 918 | y1 = b[1] * d3_geo_radians, cy1 = Math.cos(y1), sy1 = Math.sin(y1), 919 | d = interpolate.d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)))), 920 | sd = Math.sin(d); 921 | 922 | // From http://williams.best.vwh.net/avform.htm#Intermediate 923 | function interpolate(t) { 924 | var A = Math.sin(d - (t *= d)) / sd, 925 | B = Math.sin(t) / sd, 926 | x = A * cy0 * cx0 + B * cy1 * cx1, 927 | y = A * cy0 * sx0 + B * cy1 * sx1, 928 | z = A * sy0 + B * sy1; 929 | return [ 930 | Math.atan2(y, x) / d3_geo_radians, 931 | Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_geo_radians 932 | ]; 933 | } 934 | 935 | return interpolate; 936 | } 937 | d3.geo.greatCircle = d3.geo.circle; 938 | })(); 939 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/d3.chart.js: -------------------------------------------------------------------------------- 1 | (function(){d3.chart = {}; 2 | // Inspired by http://informationandvisualization.de/blog/box-plot 3 | d3.chart.box = function() { 4 | var width = 1, 5 | height = 1, 6 | duration = 0, 7 | domain = null, 8 | value = Number, 9 | whiskers = d3_chart_boxWhiskers, 10 | quartiles = d3_chart_boxQuartiles, 11 | tickFormat = null; 12 | 13 | // For each small multiple… 14 | function box(g) { 15 | g.each(function(d, i) { 16 | d = d.map(value).sort(d3.ascending); 17 | var g = d3.select(this), 18 | n = d.length, 19 | min = d[0], 20 | max = d[n - 1]; 21 | 22 | // Compute quartiles. Must return exactly 3 elements. 23 | var quartileData = d.quartiles = quartiles(d); 24 | 25 | // Compute whiskers. Must return exactly 2 elements, or null. 26 | var whiskerIndices = whiskers && whiskers.call(this, d, i), 27 | whiskerData = whiskerIndices && whiskerIndices.map(function(i) { return d[i]; }); 28 | 29 | // Compute outliers. If no whiskers are specified, all data are "outliers". 30 | // We compute the outliers as indices, so that we can join across transitions! 31 | var outlierIndices = whiskerIndices 32 | ? d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n)) 33 | : d3.range(n); 34 | 35 | // Compute the new x-scale. 36 | var x1 = d3.scale.linear() 37 | .domain(domain && domain.call(this, d, i) || [min, max]) 38 | .range([height, 0]); 39 | 40 | // Retrieve the old x-scale, if this is an update. 41 | var x0 = this.__chart__ || d3.scale.linear() 42 | .domain([0, Infinity]) 43 | .range(x1.range()); 44 | 45 | // Stash the new scale. 46 | this.__chart__ = x1; 47 | 48 | // Note: the box, median, and box tick elements are fixed in number, 49 | // so we only have to handle enter and update. In contrast, the outliers 50 | // and other elements are variable, so we need to exit them! Variable 51 | // elements also fade in and out. 52 | 53 | // Update center line: the vertical line spanning the whiskers. 54 | var center = g.selectAll("line.center") 55 | .data(whiskerData ? [whiskerData] : []); 56 | 57 | center.enter().insert("svg:line", "rect") 58 | .attr("class", "center") 59 | .attr("x1", width / 2) 60 | .attr("y1", function(d) { return x0(d[0]); }) 61 | .attr("x2", width / 2) 62 | .attr("y2", function(d) { return x0(d[1]); }) 63 | .style("opacity", 1e-6) 64 | .transition() 65 | .duration(duration) 66 | .style("opacity", 1) 67 | .attr("y1", function(d) { return x1(d[0]); }) 68 | .attr("y2", function(d) { return x1(d[1]); }); 69 | 70 | center.transition() 71 | .duration(duration) 72 | .style("opacity", 1) 73 | .attr("y1", function(d) { return x1(d[0]); }) 74 | .attr("y2", function(d) { return x1(d[1]); }); 75 | 76 | center.exit().transition() 77 | .duration(duration) 78 | .style("opacity", 1e-6) 79 | .attr("y1", function(d) { return x1(d[0]); }) 80 | .attr("y2", function(d) { return x1(d[1]); }) 81 | .remove(); 82 | 83 | // Update innerquartile box. 84 | var box = g.selectAll("rect.box") 85 | .data([quartileData]); 86 | 87 | box.enter().append("svg:rect") 88 | .attr("class", "box") 89 | .attr("x", 0) 90 | .attr("y", function(d) { return x0(d[2]); }) 91 | .attr("width", width) 92 | .attr("height", function(d) { return x0(d[0]) - x0(d[2]); }) 93 | .transition() 94 | .duration(duration) 95 | .attr("y", function(d) { return x1(d[2]); }) 96 | .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); 97 | 98 | box.transition() 99 | .duration(duration) 100 | .attr("y", function(d) { return x1(d[2]); }) 101 | .attr("height", function(d) { return x1(d[0]) - x1(d[2]); }); 102 | 103 | // Update median line. 104 | var medianLine = g.selectAll("line.median") 105 | .data([quartileData[1]]); 106 | 107 | medianLine.enter().append("svg:line") 108 | .attr("class", "median") 109 | .attr("x1", 0) 110 | .attr("y1", x0) 111 | .attr("x2", width) 112 | .attr("y2", x0) 113 | .transition() 114 | .duration(duration) 115 | .attr("y1", x1) 116 | .attr("y2", x1); 117 | 118 | medianLine.transition() 119 | .duration(duration) 120 | .attr("y1", x1) 121 | .attr("y2", x1); 122 | 123 | // Update whiskers. 124 | var whisker = g.selectAll("line.whisker") 125 | .data(whiskerData || []); 126 | 127 | whisker.enter().insert("svg:line", "circle, text") 128 | .attr("class", "whisker") 129 | .attr("x1", 0) 130 | .attr("y1", x0) 131 | .attr("x2", width) 132 | .attr("y2", x0) 133 | .style("opacity", 1e-6) 134 | .transition() 135 | .duration(duration) 136 | .attr("y1", x1) 137 | .attr("y2", x1) 138 | .style("opacity", 1); 139 | 140 | whisker.transition() 141 | .duration(duration) 142 | .attr("y1", x1) 143 | .attr("y2", x1) 144 | .style("opacity", 1); 145 | 146 | whisker.exit().transition() 147 | .duration(duration) 148 | .attr("y1", x1) 149 | .attr("y2", x1) 150 | .style("opacity", 1e-6) 151 | .remove(); 152 | 153 | // Update outliers. 154 | var outlier = g.selectAll("circle.outlier") 155 | .data(outlierIndices, Number); 156 | 157 | outlier.enter().insert("svg:circle", "text") 158 | .attr("class", "outlier") 159 | .attr("r", 5) 160 | .attr("cx", width / 2) 161 | .attr("cy", function(i) { return x0(d[i]); }) 162 | .style("opacity", 1e-6) 163 | .transition() 164 | .duration(duration) 165 | .attr("cy", function(i) { return x1(d[i]); }) 166 | .style("opacity", 1); 167 | 168 | outlier.transition() 169 | .duration(duration) 170 | .attr("cy", function(i) { return x1(d[i]); }) 171 | .style("opacity", 1); 172 | 173 | outlier.exit().transition() 174 | .duration(duration) 175 | .attr("cy", function(i) { return x1(d[i]); }) 176 | .style("opacity", 1e-6) 177 | .remove(); 178 | 179 | // Compute the tick format. 180 | var format = tickFormat || x1.tickFormat(8); 181 | 182 | // Update box ticks. 183 | var boxTick = g.selectAll("text.box") 184 | .data(quartileData); 185 | 186 | boxTick.enter().append("svg:text") 187 | .attr("class", "box") 188 | .attr("dy", ".3em") 189 | .attr("dx", function(d, i) { return i & 1 ? 6 : -6 }) 190 | .attr("x", function(d, i) { return i & 1 ? width : 0 }) 191 | .attr("y", x0) 192 | .attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; }) 193 | .text(format) 194 | .transition() 195 | .duration(duration) 196 | .attr("y", x1); 197 | 198 | boxTick.transition() 199 | .duration(duration) 200 | .text(format) 201 | .attr("y", x1); 202 | 203 | // Update whisker ticks. These are handled separately from the box 204 | // ticks because they may or may not exist, and we want don't want 205 | // to join box ticks pre-transition with whisker ticks post-. 206 | var whiskerTick = g.selectAll("text.whisker") 207 | .data(whiskerData || []); 208 | 209 | whiskerTick.enter().append("svg:text") 210 | .attr("class", "whisker") 211 | .attr("dy", ".3em") 212 | .attr("dx", 6) 213 | .attr("x", width) 214 | .attr("y", x0) 215 | .text(format) 216 | .style("opacity", 1e-6) 217 | .transition() 218 | .duration(duration) 219 | .attr("y", x1) 220 | .style("opacity", 1); 221 | 222 | whiskerTick.transition() 223 | .duration(duration) 224 | .text(format) 225 | .attr("y", x1) 226 | .style("opacity", 1); 227 | 228 | whiskerTick.exit().transition() 229 | .duration(duration) 230 | .attr("y", x1) 231 | .style("opacity", 1e-6) 232 | .remove(); 233 | }); 234 | d3.timer.flush(); 235 | } 236 | 237 | box.width = function(x) { 238 | if (!arguments.length) return width; 239 | width = x; 240 | return box; 241 | }; 242 | 243 | box.height = function(x) { 244 | if (!arguments.length) return height; 245 | height = x; 246 | return box; 247 | }; 248 | 249 | box.tickFormat = function(x) { 250 | if (!arguments.length) return tickFormat; 251 | tickFormat = x; 252 | return box; 253 | }; 254 | 255 | box.duration = function(x) { 256 | if (!arguments.length) return duration; 257 | duration = x; 258 | return box; 259 | }; 260 | 261 | box.domain = function(x) { 262 | if (!arguments.length) return domain; 263 | domain = x == null ? x : d3.functor(x); 264 | return box; 265 | }; 266 | 267 | box.value = function(x) { 268 | if (!arguments.length) return value; 269 | value = x; 270 | return box; 271 | }; 272 | 273 | box.whiskers = function(x) { 274 | if (!arguments.length) return whiskers; 275 | whiskers = x; 276 | return box; 277 | }; 278 | 279 | box.quartiles = function(x) { 280 | if (!arguments.length) return quartiles; 281 | quartiles = x; 282 | return box; 283 | }; 284 | 285 | return box; 286 | }; 287 | 288 | function d3_chart_boxWhiskers(d) { 289 | return [0, d.length - 1]; 290 | } 291 | 292 | function d3_chart_boxQuartiles(d) { 293 | return [ 294 | d3.quantile(d, .25), 295 | d3.quantile(d, .5), 296 | d3.quantile(d, .75) 297 | ]; 298 | } 299 | // Chart design based on the recommendations of Stephen Few. Implementation 300 | // based on the work of Clint Ivy, Jamie Love, and Jason Davies. 301 | // http://projects.instantcognition.com/protovis/bulletchart/ 302 | d3.chart.bullet = function() { 303 | var orient = "left", // TODO top & bottom 304 | reverse = false, 305 | duration = 0, 306 | ranges = d3_chart_bulletRanges, 307 | markers = d3_chart_bulletMarkers, 308 | measures = d3_chart_bulletMeasures, 309 | width = 380, 310 | height = 30, 311 | tickFormat = null; 312 | 313 | // For each small multiple… 314 | function bullet(g) { 315 | g.each(function(d, i) { 316 | var rangez = ranges.call(this, d, i).slice().sort(d3.descending), 317 | markerz = markers.call(this, d, i).slice().sort(d3.descending), 318 | measurez = measures.call(this, d, i).slice().sort(d3.descending), 319 | g = d3.select(this); 320 | 321 | // Compute the new x-scale. 322 | var x1 = d3.scale.linear() 323 | .domain([0, Math.max(rangez[0], markerz[0], measurez[0])]) 324 | .range(reverse ? [width, 0] : [0, width]); 325 | 326 | // Retrieve the old x-scale, if this is an update. 327 | var x0 = this.__chart__ || d3.scale.linear() 328 | .domain([0, Infinity]) 329 | .range(x1.range()); 330 | 331 | // Stash the new scale. 332 | this.__chart__ = x1; 333 | 334 | // Derive width-scales from the x-scales. 335 | var w0 = d3_chart_bulletWidth(x0), 336 | w1 = d3_chart_bulletWidth(x1); 337 | 338 | // Update the range rects. 339 | var range = g.selectAll("rect.range") 340 | .data(rangez); 341 | 342 | range.enter().append("svg:rect") 343 | .attr("class", function(d, i) { return "range s" + i; }) 344 | .attr("width", w0) 345 | .attr("height", height) 346 | .attr("x", reverse ? x0 : 0) 347 | .transition() 348 | .duration(duration) 349 | .attr("width", w1) 350 | .attr("x", reverse ? x1 : 0); 351 | 352 | range.transition() 353 | .duration(duration) 354 | .attr("x", reverse ? x1 : 0) 355 | .attr("width", w1) 356 | .attr("height", height); 357 | 358 | // Update the measure rects. 359 | var measure = g.selectAll("rect.measure") 360 | .data(measurez); 361 | 362 | measure.enter().append("svg:rect") 363 | .attr("class", function(d, i) { return "measure s" + i; }) 364 | .attr("width", w0) 365 | .attr("height", height / 3) 366 | .attr("x", reverse ? x0 : 0) 367 | .attr("y", height / 3) 368 | .transition() 369 | .duration(duration) 370 | .attr("width", w1) 371 | .attr("x", reverse ? x1 : 0); 372 | 373 | measure.transition() 374 | .duration(duration) 375 | .attr("width", w1) 376 | .attr("height", height / 3) 377 | .attr("x", reverse ? x1 : 0) 378 | .attr("y", height / 3); 379 | 380 | // Update the marker lines. 381 | var marker = g.selectAll("line.marker") 382 | .data(markerz); 383 | 384 | marker.enter().append("svg:line") 385 | .attr("class", "marker") 386 | .attr("x1", x0) 387 | .attr("x2", x0) 388 | .attr("y1", height / 6) 389 | .attr("y2", height * 5 / 6) 390 | .transition() 391 | .duration(duration) 392 | .attr("x1", x1) 393 | .attr("x2", x1); 394 | 395 | marker.transition() 396 | .duration(duration) 397 | .attr("x1", x1) 398 | .attr("x2", x1) 399 | .attr("y1", height / 6) 400 | .attr("y2", height * 5 / 6); 401 | 402 | // Compute the tick format. 403 | var format = tickFormat || x1.tickFormat(8); 404 | 405 | // Update the tick groups. 406 | var tick = g.selectAll("g.tick") 407 | .data(x1.ticks(8), function(d) { 408 | return this.textContent || format(d); 409 | }); 410 | 411 | // Initialize the ticks with the old scale, x0. 412 | var tickEnter = tick.enter().append("svg:g") 413 | .attr("class", "tick") 414 | .attr("transform", d3_chart_bulletTranslate(x0)) 415 | .style("opacity", 1e-6); 416 | 417 | tickEnter.append("svg:line") 418 | .attr("y1", height) 419 | .attr("y2", height * 7 / 6); 420 | 421 | tickEnter.append("svg:text") 422 | .attr("text-anchor", "middle") 423 | .attr("dy", "1em") 424 | .attr("y", height * 7 / 6) 425 | .text(format); 426 | 427 | // Transition the entering ticks to the new scale, x1. 428 | tickEnter.transition() 429 | .duration(duration) 430 | .attr("transform", d3_chart_bulletTranslate(x1)) 431 | .style("opacity", 1); 432 | 433 | // Transition the updating ticks to the new scale, x1. 434 | var tickUpdate = tick.transition() 435 | .duration(duration) 436 | .attr("transform", d3_chart_bulletTranslate(x1)) 437 | .style("opacity", 1); 438 | 439 | tickUpdate.select("line") 440 | .attr("y1", height) 441 | .attr("y2", height * 7 / 6); 442 | 443 | tickUpdate.select("text") 444 | .attr("y", height * 7 / 6); 445 | 446 | // Transition the exiting ticks to the new scale, x1. 447 | tick.exit().transition() 448 | .duration(duration) 449 | .attr("transform", d3_chart_bulletTranslate(x1)) 450 | .style("opacity", 1e-6) 451 | .remove(); 452 | }); 453 | d3.timer.flush(); 454 | } 455 | 456 | // left, right, top, bottom 457 | bullet.orient = function(x) { 458 | if (!arguments.length) return orient; 459 | orient = x; 460 | reverse = orient == "right" || orient == "bottom"; 461 | return bullet; 462 | }; 463 | 464 | // ranges (bad, satisfactory, good) 465 | bullet.ranges = function(x) { 466 | if (!arguments.length) return ranges; 467 | ranges = x; 468 | return bullet; 469 | }; 470 | 471 | // markers (previous, goal) 472 | bullet.markers = function(x) { 473 | if (!arguments.length) return markers; 474 | markers = x; 475 | return bullet; 476 | }; 477 | 478 | // measures (actual, forecast) 479 | bullet.measures = function(x) { 480 | if (!arguments.length) return measures; 481 | measures = x; 482 | return bullet; 483 | }; 484 | 485 | bullet.width = function(x) { 486 | if (!arguments.length) return width; 487 | width = x; 488 | return bullet; 489 | }; 490 | 491 | bullet.height = function(x) { 492 | if (!arguments.length) return height; 493 | height = x; 494 | return bullet; 495 | }; 496 | 497 | bullet.tickFormat = function(x) { 498 | if (!arguments.length) return tickFormat; 499 | tickFormat = x; 500 | return bullet; 501 | }; 502 | 503 | bullet.duration = function(x) { 504 | if (!arguments.length) return duration; 505 | duration = x; 506 | return bullet; 507 | }; 508 | 509 | return bullet; 510 | }; 511 | 512 | function d3_chart_bulletRanges(d) { 513 | return d.ranges; 514 | } 515 | 516 | function d3_chart_bulletMarkers(d) { 517 | return d.markers; 518 | } 519 | 520 | function d3_chart_bulletMeasures(d) { 521 | return d.measures; 522 | } 523 | 524 | function d3_chart_bulletTranslate(x) { 525 | return function(d) { 526 | return "translate(" + x(d) + ",0)"; 527 | }; 528 | } 529 | 530 | function d3_chart_bulletWidth(x) { 531 | var x0 = x(0); 532 | return function(d) { 533 | return Math.abs(x(d) - x0); 534 | }; 535 | } 536 | // Implements a horizon layout, which is a variation of a single-series 537 | // area chart where the area is folded into multiple bands. Color is used to 538 | // encode band, allowing the size of the chart to be reduced significantly 539 | // without impeding readability. This layout algorithm is based on the work of 540 | // J. Heer, N. Kong and M. Agrawala in "Sizing the Horizon: The Effects of Chart 541 | // Size and Layering on the Graphical Perception of Time Series Visualizations", 542 | // CHI 2009. http://hci.stanford.edu/publications/2009/heer-horizon-chi09.pdf 543 | d3.chart.horizon = function() { 544 | var bands = 1, // between 1 and 5, typically 545 | mode = "offset", // or mirror 546 | interpolate = "linear", // or basis, monotone, step-before, etc. 547 | x = d3_chart_horizonX, 548 | y = d3_chart_horizonY, 549 | w = 960, 550 | h = 40, 551 | duration = 0; 552 | 553 | var color = d3.scale.linear() 554 | .domain([-1, 0, 1]) 555 | .range(["#d62728", "#fff", "#1f77b4"]); 556 | 557 | // For each small multiple… 558 | function horizon(g) { 559 | g.each(function(d, i) { 560 | var g = d3.select(this), 561 | n = 2 * bands + 1, 562 | xMin = Infinity, 563 | xMax = -Infinity, 564 | yMax = -Infinity, 565 | x0, // old x-scale 566 | y0, // old y-scale 567 | id; // unique id for paths 568 | 569 | // Compute x- and y-values along with extents. 570 | var data = d.map(function(d, i) { 571 | var xv = x.call(this, d, i), 572 | yv = y.call(this, d, i); 573 | if (xv < xMin) xMin = xv; 574 | if (xv > xMax) xMax = xv; 575 | if (-yv > yMax) yMax = -yv; 576 | if (yv > yMax) yMax = yv; 577 | return [xv, yv]; 578 | }); 579 | 580 | // Compute the new x- and y-scales. 581 | var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, w]), 582 | y1 = d3.scale.linear().domain([0, yMax]).range([0, h * bands]); 583 | 584 | // Retrieve the old scales, if this is an update. 585 | if (this.__chart__) { 586 | x0 = this.__chart__.x; 587 | y0 = this.__chart__.y; 588 | id = this.__chart__.id; 589 | } else { 590 | x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range()); 591 | y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range()); 592 | id = ++d3_chart_horizonId; 593 | } 594 | 595 | // We'll use a defs to store the area path and the clip path. 596 | var defs = g.selectAll("defs") 597 | .data([data]); 598 | 599 | var defsEnter = defs.enter().append("svg:defs"); 600 | 601 | // The clip path is a simple rect. 602 | defsEnter.append("svg:clipPath") 603 | .attr("id", "d3_chart_horizon_clip" + id) 604 | .append("svg:rect") 605 | .attr("width", w) 606 | .attr("height", h); 607 | 608 | defs.select("rect").transition() 609 | .duration(duration) 610 | .attr("width", w) 611 | .attr("height", h); 612 | 613 | // The area path is rendered with our resuable d3.svg.area. 614 | defsEnter.append("svg:path") 615 | .attr("id", "d3_chart_horizon_path" + id) 616 | .attr("d", d3_chart_horizonArea 617 | .interpolate(interpolate) 618 | .x(function(d) { return x0(d[0]); }) 619 | .y0(h * bands) 620 | .y1(function(d) { return h * bands - y0(d[1]); })) 621 | .transition() 622 | .duration(duration) 623 | .attr("d", d3_chart_horizonArea 624 | .x(function(d) { return x1(d[0]); }) 625 | .y1(function(d) { return h * bands - y1(d[1]); })); 626 | 627 | defs.select("path").transition() 628 | .duration(duration) 629 | .attr("d", d3_chart_horizonArea); 630 | 631 | // We'll use a container to clip all horizon layers at once. 632 | g.selectAll("g") 633 | .data([null]) 634 | .enter().append("svg:g") 635 | .attr("clip-path", "url(#d3_chart_horizon_clip" + id + ")"); 636 | 637 | // Define the transform function based on the mode. 638 | var transform = mode == "offset" 639 | ? function(d) { return "translate(0," + (d + (d < 0) - bands) * h + ")"; } 640 | : function(d) { return (d < 0 ? "scale(1,-1)" : "") + "translate(0," + (d - bands) * h + ")"; }; 641 | 642 | // Instantiate each copy of the path with different transforms. 643 | var u = g.select("g").selectAll("use") 644 | .data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number); 645 | 646 | // TODO don't fudge the enter transition 647 | u.enter().append("svg:use") 648 | .attr("xlink:href", "#d3_chart_horizon_path" + id) 649 | .attr("transform", function(d) { return transform(d + (d > 0 ? 1 : -1)); }) 650 | .style("fill", color) 651 | .transition() 652 | .duration(duration) 653 | .attr("transform", transform); 654 | 655 | u.transition() 656 | .duration(duration) 657 | .attr("transform", transform) 658 | .style("fill", color); 659 | 660 | u.exit().transition() 661 | .duration(duration) 662 | .attr("transform", transform) 663 | .remove(); 664 | 665 | // Stash the new scales. 666 | this.__chart__ = {x: x1, y: y1, id: id}; 667 | }); 668 | d3.timer.flush(); 669 | } 670 | 671 | horizon.duration = function(x) { 672 | if (!arguments.length) return duration; 673 | duration = +x; 674 | return horizon; 675 | }; 676 | 677 | horizon.bands = function(x) { 678 | if (!arguments.length) return bands; 679 | bands = +x; 680 | color.domain([-bands, 0, bands]); 681 | return horizon; 682 | }; 683 | 684 | horizon.mode = function(x) { 685 | if (!arguments.length) return mode; 686 | mode = x + ""; 687 | return horizon; 688 | }; 689 | 690 | horizon.colors = function(x) { 691 | if (!arguments.length) return color.range(); 692 | color.range(x); 693 | return horizon; 694 | }; 695 | 696 | horizon.interpolate = function(x) { 697 | if (!arguments.length) return interpolate; 698 | interpolate = x + ""; 699 | return horizon; 700 | }; 701 | 702 | horizon.x = function(z) { 703 | if (!arguments.length) return x; 704 | x = z; 705 | return horizon; 706 | }; 707 | 708 | horizon.y = function(z) { 709 | if (!arguments.length) return y; 710 | y = z; 711 | return horizon; 712 | }; 713 | 714 | horizon.width = function(x) { 715 | if (!arguments.length) return w; 716 | w = +x; 717 | return horizon; 718 | }; 719 | 720 | horizon.height = function(x) { 721 | if (!arguments.length) return h; 722 | h = +x; 723 | return horizon; 724 | }; 725 | 726 | return horizon; 727 | }; 728 | 729 | var d3_chart_horizonArea = d3.svg.area(), 730 | d3_chart_horizonId = 0; 731 | 732 | function d3_chart_horizonX(d) { 733 | return d[0]; 734 | } 735 | 736 | function d3_chart_horizonY(d) { 737 | return d[1]; 738 | } 739 | // Based on http://vis.stanford.edu/protovis/ex/qqplot.html 740 | d3.chart.qq = function() { 741 | var width = 1, 742 | height = 1, 743 | duration = 0, 744 | domain = null, 745 | tickFormat = null, 746 | n = 100, 747 | x = d3_chart_qqX, 748 | y = d3_chart_qqY; 749 | 750 | // For each small multiple… 751 | function qq(g) { 752 | g.each(function(d, i) { 753 | var g = d3.select(this), 754 | qx = d3_chart_qqQuantiles(n, x.call(this, d, i)), 755 | qy = d3_chart_qqQuantiles(n, y.call(this, d, i)), 756 | xd = domain && domain.call(this, d, i) || [d3.min(qx), d3.max(qx)], // new x-domain 757 | yd = domain && domain.call(this, d, i) || [d3.min(qy), d3.max(qy)], // new y-domain 758 | x0, // old x-scale 759 | y0; // old y-scale 760 | 761 | // Compute the new x-scale. 762 | var x1 = d3.scale.linear() 763 | .domain(xd) 764 | .range([0, width]); 765 | 766 | // Compute the new y-scale. 767 | var y1 = d3.scale.linear() 768 | .domain(yd) 769 | .range([height, 0]); 770 | 771 | // Retrieve the old scales, if this is an update. 772 | if (this.__chart__) { 773 | x0 = this.__chart__.x; 774 | y0 = this.__chart__.y; 775 | } else { 776 | x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range()); 777 | y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range()); 778 | } 779 | 780 | // Stash the new scales. 781 | this.__chart__ = {x: x1, y: y1}; 782 | 783 | // Update diagonal line. 784 | var diagonal = g.selectAll("line.diagonal") 785 | .data([null]); 786 | 787 | diagonal.enter().append("svg:line") 788 | .attr("class", "diagonal") 789 | .attr("x1", x1(yd[0])) 790 | .attr("y1", y1(xd[0])) 791 | .attr("x2", x1(yd[1])) 792 | .attr("y2", y1(xd[1])); 793 | 794 | diagonal.transition() 795 | .duration(duration) 796 | .attr("x1", x1(yd[0])) 797 | .attr("y1", y1(xd[0])) 798 | .attr("x2", x1(yd[1])) 799 | .attr("y2", y1(xd[1])); 800 | 801 | // Update quantile plots. 802 | var circle = g.selectAll("circle") 803 | .data(d3.range(n).map(function(i) { 804 | return {x: qx[i], y: qy[i]}; 805 | })); 806 | 807 | circle.enter().append("svg:circle") 808 | .attr("class", "quantile") 809 | .attr("r", 4.5) 810 | .attr("cx", function(d) { return x0(d.x); }) 811 | .attr("cy", function(d) { return y0(d.y); }) 812 | .style("opacity", 1e-6) 813 | .transition() 814 | .duration(duration) 815 | .attr("cx", function(d) { return x1(d.x); }) 816 | .attr("cy", function(d) { return y1(d.y); }) 817 | .style("opacity", 1); 818 | 819 | circle.transition() 820 | .duration(duration) 821 | .attr("cx", function(d) { return x1(d.x); }) 822 | .attr("cy", function(d) { return y1(d.y); }) 823 | .style("opacity", 1); 824 | 825 | circle.exit().transition() 826 | .duration(duration) 827 | .attr("cx", function(d) { return x1(d.x); }) 828 | .attr("cy", function(d) { return y1(d.y); }) 829 | .style("opacity", 1e-6) 830 | .remove(); 831 | 832 | var xformat = tickFormat || x1.tickFormat(4), 833 | yformat = tickFormat || y1.tickFormat(4), 834 | tx = function(d) { return "translate(" + x1(d) + "," + height + ")"; }, 835 | ty = function(d) { return "translate(0," + y1(d) + ")"; }; 836 | 837 | // Update x-ticks. 838 | var xtick = g.selectAll("g.x.tick") 839 | .data(x1.ticks(4), function(d) { 840 | return this.textContent || xformat(d); 841 | }); 842 | 843 | var xtickEnter = xtick.enter().append("svg:g") 844 | .attr("class", "x tick") 845 | .attr("transform", function(d) { return "translate(" + x0(d) + "," + height + ")"; }) 846 | .style("opacity", 1e-6); 847 | 848 | xtickEnter.append("svg:line") 849 | .attr("y1", 0) 850 | .attr("y2", -6); 851 | 852 | xtickEnter.append("svg:text") 853 | .attr("text-anchor", "middle") 854 | .attr("dy", "1em") 855 | .text(xformat); 856 | 857 | // Transition the entering ticks to the new scale, x1. 858 | xtickEnter.transition() 859 | .duration(duration) 860 | .attr("transform", tx) 861 | .style("opacity", 1); 862 | 863 | // Transition the updating ticks to the new scale, x1. 864 | xtick.transition() 865 | .duration(duration) 866 | .attr("transform", tx) 867 | .style("opacity", 1); 868 | 869 | // Transition the exiting ticks to the new scale, x1. 870 | xtick.exit().transition() 871 | .duration(duration) 872 | .attr("transform", tx) 873 | .style("opacity", 1e-6) 874 | .remove(); 875 | 876 | // Update ticks. 877 | var ytick = g.selectAll("g.y.tick") 878 | .data(y1.ticks(4), function(d) { 879 | return this.textContent || yformat(d); 880 | }); 881 | 882 | var ytickEnter = ytick.enter().append("svg:g") 883 | .attr("class", "y tick") 884 | .attr("transform", function(d) { return "translate(0," + y0(d) + ")"; }) 885 | .style("opacity", 1e-6); 886 | 887 | ytickEnter.append("svg:line") 888 | .attr("x1", 0) 889 | .attr("x2", 6); 890 | 891 | ytickEnter.append("svg:text") 892 | .attr("text-anchor", "end") 893 | .attr("dx", "-.5em") 894 | .attr("dy", ".3em") 895 | .text(yformat); 896 | 897 | // Transition the entering ticks to the new scale, y1. 898 | ytickEnter.transition() 899 | .duration(duration) 900 | .attr("transform", ty) 901 | .style("opacity", 1); 902 | 903 | // Transition the updating ticks to the new scale, y1. 904 | ytick.transition() 905 | .duration(duration) 906 | .attr("transform", ty) 907 | .style("opacity", 1); 908 | 909 | // Transition the exiting ticks to the new scale, y1. 910 | ytick.exit().transition() 911 | .duration(duration) 912 | .attr("transform", ty) 913 | .style("opacity", 1e-6) 914 | .remove(); 915 | }); 916 | } 917 | 918 | qq.width = function(x) { 919 | if (!arguments.length) return width; 920 | width = x; 921 | return qq; 922 | }; 923 | 924 | qq.height = function(x) { 925 | if (!arguments.length) return height; 926 | height = x; 927 | return qq; 928 | }; 929 | 930 | qq.duration = function(x) { 931 | if (!arguments.length) return duration; 932 | duration = x; 933 | return qq; 934 | }; 935 | 936 | qq.domain = function(x) { 937 | if (!arguments.length) return domain; 938 | domain = x == null ? x : d3.functor(x); 939 | return qq; 940 | }; 941 | 942 | qq.count = function(z) { 943 | if (!arguments.length) return n; 944 | n = z; 945 | return qq; 946 | }; 947 | 948 | qq.x = function(z) { 949 | if (!arguments.length) return x; 950 | x = z; 951 | return qq; 952 | }; 953 | 954 | qq.y = function(z) { 955 | if (!arguments.length) return y; 956 | y = z; 957 | return qq; 958 | }; 959 | 960 | qq.tickFormat = function(x) { 961 | if (!arguments.length) return tickFormat; 962 | tickFormat = x; 963 | return qq; 964 | }; 965 | 966 | return qq; 967 | }; 968 | 969 | function d3_chart_qqQuantiles(n, values) { 970 | var m = values.length - 1; 971 | values = values.slice().sort(d3.ascending); 972 | return d3.range(n).map(function(i) { 973 | return values[~~(i * m / n)]; 974 | }); 975 | } 976 | 977 | function d3_chart_qqX(d) { 978 | return d.x; 979 | } 980 | 981 | function d3_chart_qqY(d) { 982 | return d.y; 983 | } 984 | })(); 985 | --------------------------------------------------------------------------------