d3_rails 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ (function(){function n(a){return a.y}function m(a){return a.x}function l(a,b){var c=b.length-1;b=b.slice().sort(d3.ascending);return d3.range(a).map(function(d){return b[~~(d*c/a)]})}function k(a){return a[1]}function j(a){return a[0]}function g(a){var b=a(0);return function(c){return Math.abs(a(c)-b)}}function f(a){return function(b){return"translate("+a(b)+",0)"}}function e(a){return a.measures}function d(a){return a.markers}function c(a){return a.ranges}function b(a){return[d3.quantile(a,.25),d3.quantile(a,.5),d3.quantile(a,.75)]}function a(a){return[0,a.length-1]}d3.chart={},d3.chart.box=function(){function k(a){a.each(function(a,b){a=a.map(g).sort(d3.ascending);var k=d3.select(this),l=a.length,m=a[0],n=a[l-1],o=a.quartiles=i(a),p=h&&h.call(this,a,b),q=p&&p.map(function(b){return a[b]}),r=p?d3.range(0,p[0]).concat(d3.range(p[1]+1,l)):d3.range(l),s=d3.scale.linear().domain(f&&f.call(this,a,b)||[m,n]).range([d,0]),t=this.__chart__||d3.scale.linear().domain([0,Infinity]).range(s.range());this.__chart__=s;var u=k.selectAll("line.center").data(q?[q]:[]);u.enter().insert("svg:line","rect").attr("class","center").attr("x1",c/2).attr("y1",function(a){return t(a[0])}).attr("x2",c/2).attr("y2",function(a){return t(a[1])}).style("opacity",1e-6).transition().duration(e).style("opacity",1).attr("y1",function(a){return s(a[0])}).attr("y2",function(a){return s(a[1])}),u.transition().duration(e).style("opacity",1).attr("y1",function(a){return s(a[0])}).attr("y2",function(a){return s(a[1])}),u.exit().transition().duration(e).style("opacity",1e-6).attr("y1",function(a){return s(a[0])}).attr("y2",function(a){return s(a[1])}).remove();var v=k.selectAll("rect.box").data([o]);v.enter().append("svg:rect").attr("class","box").attr("x",0).attr("y",function(a){return t(a[2])}).attr("width",c).attr("height",function(a){return t(a[0])-t(a[2])}).transition().duration(e).attr("y",function(a){return s(a[2])}).attr("height",function(a){return s(a[0])-s(a[2])}),v.transition().duration(e).attr("y",function(a){return s(a[2])}).attr("height",function(a){return s(a[0])-s(a[2])});var w=k.selectAll("line.median").data([o[1]]);w.enter().append("svg:line").attr("class","median").attr("x1",0).attr("y1",t).attr("x2",c).attr("y2",t).transition().duration(e).attr("y1",s).attr("y2",s),w.transition().duration(e).attr("y1",s).attr("y2",s);var x=k.selectAll("line.whisker").data(q||[]);x.enter().insert("svg:line","circle, text").attr("class","whisker").attr("x1",0).attr("y1",t).attr("x2",c).attr("y2",t).style("opacity",1e-6).transition().duration(e).attr("y1",s).attr("y2",s).style("opacity",1),x.transition().duration(e).attr("y1",s).attr("y2",s).style("opacity",1),x.exit().transition().duration(e).attr("y1",s).attr("y2",s).style("opacity",1e-6).remove();var y=k.selectAll("circle.outlier").data(r,Number);y.enter().insert("svg:circle","text").attr("class","outlier").attr("r",5).attr("cx",c/2).attr("cy",function(b){return t(a[b])}).style("opacity",1e-6).transition().duration(e).attr("cy",function(b){return s(a[b])}).style("opacity",1),y.transition().duration(e).attr("cy",function(b){return s(a[b])}).style("opacity",1),y.exit().transition().duration(e).attr("cy",function(b){return s(a[b])}).style("opacity",1e-6).remove();var z=j||s.tickFormat(8),A=k.selectAll("text.box").data(o);A.enter().append("svg:text").attr("class","box").attr("dy",".3em").attr("dx",function(a,b){return b&1?6:-6}).attr("x",function(a,b){return b&1?c:0}).attr("y",t).attr("text-anchor",function(a,b){return b&1?"start":"end"}).text(z).transition().duration(e).attr("y",s),A.transition().duration(e).text(z).attr("y",s);var B=k.selectAll("text.whisker").data(q||[]);B.enter().append("svg:text").attr("class","whisker").attr("dy",".3em").attr("dx",6).attr("x",c).attr("y",t).text(z).style("opacity",1e-6).transition().duration(e).attr("y",s).style("opacity",1),B.transition().duration(e).text(z).attr("y",s).style("opacity",1),B.exit().transition().duration(e).attr("y",s).style("opacity",1e-6).remove()}),d3.timer.flush()}var c=1,d=1,e=0,f=null,g=Number,h=a,i=b,j=null;k.width=function(a){if(!arguments.length)return c;c=a;return k},k.height=function(a){if(!arguments.length)return d;d=a;return k},k.tickFormat=function(a){if(!arguments.length)return j;j=a;return k},k.duration=function(a){if(!arguments.length)return e;e=a;return k},k.domain=function(a){if(!arguments.length)return f;f=a==null?a:d3.functor(a);return k},k.value=function(a){if(!arguments.length)return g;g=a;return k},k.whiskers=function(a){if(!arguments.length)return h;h=a;return k},k.quartiles=function(a){if(!arguments.length)return i;i=a;return k};return k},d3.chart.bullet=function(){function o(a){a.each(function(a,c){var d=i.call(this,a,c).slice().sort(d3.descending),e=j.call(this,a,c).slice().sort(d3.descending),o=k.call(this,a,c).slice().sort(d3.descending),p=d3.select(this),q=d3.scale.linear().domain([0,Math.max(d[0],e[0],o[0])]).range(b?[l,0]:[0,l]),r=this.__chart__||d3.scale.linear().domain([0,Infinity]).range(q.range());this.__chart__=q;var s=g(r),t=g(q),u=p.selectAll("rect.range").data(d);u.enter().append("svg:rect").attr("class",function(a,b){return"range s"+b}).attr("width",s).attr("height",m).attr("x",b?r:0).transition().duration(h).attr("width",t).attr("x",b?q:0),u.transition().duration(h).attr("x",b?q:0).attr("width",t).attr("height",m);var v=p.selectAll("rect.measure").data(o);v.enter().append("svg:rect").attr("class",function(a,b){return"measure s"+b}).attr("width",s).attr("height",m/3).attr("x",b?r:0).attr("y",m/3).transition().duration(h).attr("width",t).attr("x",b?q:0),v.transition().duration(h).attr("width",t).attr("height",m/3).attr("x",b?q:0).attr("y",m/3);var w=p.selectAll("line.marker").data(e);w.enter().append("svg:line").attr("class","marker").attr("x1",r).attr("x2",r).attr("y1",m/6).attr("y2",m*5/6).transition().duration(h).attr("x1",q).attr("x2",q),w.transition().duration(h).attr("x1",q).attr("x2",q).attr("y1",m/6).attr("y2",m*5/6);var x=n||q.tickFormat(8),y=p.selectAll("g.tick").data(q.ticks(8),function(a){return this.textContent||x(a)}),z=y.enter().append("svg:g").attr("class","tick").attr("transform",f(r)).style("opacity",1e-6);z.append("svg:line").attr("y1",m).attr("y2",m*7/6),z.append("svg:text").attr("text-anchor","middle").attr("dy","1em").attr("y",m*7/6).text(x),z.transition().duration(h).attr("transform",f(q)).style("opacity",1);var A=y.transition().duration(h).attr("transform",f(q)).style("opacity",1);A.select("line").attr("y1",m).attr("y2",m*7/6),A.select("text").attr("y",m*7/6),y.exit().transition().duration(h).attr("transform",f(q)).style("opacity",1e-6).remove()}),d3.timer.flush()}var a="left",b=!1,h=0,i=c,j=d,k=e,l=380,m=30,n=null;o.orient=function(c){if(!arguments.length)return a;a=c,b=a=="right"||a=="bottom";return o},o.ranges=function(a){if(!arguments.length)return i;i=a;return o},o.markers=function(a){if(!arguments.length)return j;j=a;return o},o.measures=function(a){if(!arguments.length)return k;k=a;return o},o.width=function(a){if(!arguments.length)return l;l=a;return o},o.height=function(a){if(!arguments.length)return m;m=a;return o},o.tickFormat=function(a){if(!arguments.length)return n;n=a;return o},o.duration=function(a){if(!arguments.length)return h;h=a;return o};return o},d3.chart.horizon=function(){function n(j){j.each(function(j,k){var n=d3.select(this),o=2*a+1,p=Infinity,q=-Infinity,r=-Infinity,s,t,u,v=j.map(function(a,b){var c=d.call(this,a,b),f=e.call(this,a,b);c<p&&(p=c),c>q&&(q=c),-f>r&&(r=-f),f>r&&(r=f);return[c,f]}),z=d3.scale.linear().domain([p,q]).range([0,f]),A=d3.scale.linear().domain([0,r]).range([0,g*a]);this.__chart__?(s=this.__chart__.x,t=this.__chart__.y,u=this.__chart__.id):(s=d3.scale.linear().domain([0,Infinity]).range(z.range()),t=d3.scale.linear().domain([0,Infinity]).range(A.range()),u=++i);var B=n.selectAll("defs").data([v]),C=B.enter().append("svg:defs");C.append("svg:clipPath").attr("id","d3_chart_horizon_clip"+u).append("svg:rect").attr("width",f).attr("height",g),B.select("rect").transition().duration(l).attr("width",f).attr("height",g),C.append("svg:path").attr("id","d3_chart_horizon_path"+u).attr("d",h.interpolate(c).x(function(a){return s(a[0])}).y0(g*a).y1(function(b){return g*a-t(b[1])})).transition().duration(l).attr("d",h.x(function(a){return z(a[0])}).y1(function(b){return g*a-A(b[1])})),B.select("path").transition().duration(l).attr("d",h),n.selectAll("g").data([null]).enter().append("svg:g").attr("clip-path","url(#d3_chart_horizon_clip"+u+")");var D=b=="offset"?function(b){return"translate(0,"+(b+(b<0)-a)*g+")"}:function(b){return(b<0?"scale(1,-1)":"")+"translate(0,"+(b-a)*g+")"},E=n.select("g").selectAll("use").data(d3.range(-1,-a-1,-1).concat(d3.range(1,a+1)),Number);E.enter().append("svg:use").attr("xlink:href","#d3_chart_horizon_path"+u).attr("transform",function(a){return D(a+(a>0?1:-1))}).style("fill",m).transition().duration(l).attr("transform",D),E.transition().duration(l).attr("transform",D).style("fill",m),E.exit().transition().duration(l).attr("transform",D).remove(),this.__chart__={x:z,y:A,id:u}}),d3.timer.flush()}var a=1,b="offset",c="linear",d=j,e=k,f=960,g=40,l=0,m=d3.scale.linear().domain([-1,0,1]).range(["#d62728","#fff","#1f77b4"]);n.duration=function(a){if(!arguments.length)return l;l=+a;return n},n.bands=function(b){if(!arguments.length)return a;a=+b,m.domain([-a,0,a]);return n},n.mode=function(a){if(!arguments.length)return b;b=a+"";return n},n.colors=function(a){if(!arguments.length)return m.range();m.range(a);return n},n.interpolate=function(a){if(!arguments.length)return c;c=a+"";return n},n.x=function(a){if(!arguments.length)return d;d=a;return n},n.y=function(a){if(!arguments.length)return e;e=a;return n},n.width=function(a){if(!arguments.length)return f;f=+a;return n},n.height=function(a){if(!arguments.length)return g;g=+a;return n};return n};var h=d3.svg.area(),i=0;d3.chart.qq=function(){function i(i){i.each(function(i,j){var k=d3.select(this),m=l(f,g.call(this,i,j)),n=l(f,h.call(this,i,j)),o=d&&d.call(this,i,j)||[d3.min(m),d3.max(m)],p=d&&d.call(this,i,j)||[d3.min(n),d3.max(n)],q,r,s=d3.scale.linear().domain(o).range([0,a]),t=d3.scale.linear().domain(p).range([b,0]);this.__chart__?(q=this.__chart__.x,r=this.__chart__.y):(q=d3.scale.linear().domain([0,Infinity]).range(s.range()),r=d3.scale.linear().domain([0,Infinity]).range(t.range())),this.__chart__={x:s,y:t};var u=k.selectAll("line.diagonal").data([null]);u.enter().append("svg:line").attr("class","diagonal").attr("x1",s(p[0])).attr("y1",t(o[0])).attr("x2",s(p[1])).attr("y2",t(o[1])),u.transition().duration(c).attr("x1",s(p[0])).attr("y1",t(o[0])).attr("x2",s(p[1])).attr("y2",t(o[1]));var v=k.selectAll("circle").data(d3.range(f).map(function(a){return{x:m[a],y:n[a]}}));v.enter().append("svg:circle").attr("class","quantile").attr("r",4.5).attr("cx",function(a){return q(a.x)}).attr("cy",function(a){return r(a.y)}).style("opacity",1e-6).transition().duration(c).attr("cx",function(a){return s(a.x)}).attr("cy",function(a){return t(a.y)}).style("opacity",1),v.transition().duration(c).attr("cx",function(a){return s(a.x)}).attr("cy",function(a){return t(a.y)}).style("opacity",1),v.exit().transition().duration(c).attr("cx",function(a){return s(a.x)}).attr("cy",function(a){return t(a.y)}).style("opacity",1e-6).remove();var w=e||s.tickFormat(4),z=e||t.tickFormat(4),A=function(a){return"translate("+s(a)+","+b+")"},B=function(a){return"translate(0,"+t(a)+")"},C=k.selectAll("g.x.tick").data(s.ticks(4),function(a){return this.textContent||w(a)}),D=C.enter().append("svg:g").attr("class","x tick").attr("transform",function(a){return"translate("+q(a)+","+b+")"}).style("opacity",1e-6);D.append("svg:line").attr("y1",0).attr("y2",-6),D.append("svg:text").attr("text-anchor","middle").attr("dy","1em").text(w),D.transition().duration(c).attr("transform",A).style("opacity",1),C.transition().duration(c).attr("transform",A).style("opacity",1),C.exit().transition().duration(c).attr("transform",A).style("opacity",1e-6).remove();var E=k.selectAll("g.y.tick").data(t.ticks(4),function(a){return this.textContent||z(a)}),F=E.enter().append("svg:g").attr("class","y tick").attr("transform",function(a){return"translate(0,"+r(a)+")"}).style("opacity",1e-6);F.append("svg:line").attr("x1",0).attr("x2",6),F.append("svg:text").attr("text-anchor","end").attr("dx","-.5em").attr("dy",".3em").text(z),F.transition().duration(c).attr("transform",B).style("opacity",1),E.transition().duration(c).attr("transform",B).style("opacity",1),E.exit().transition().duration(c).attr("transform",B).style("opacity",1e-6).remove()})}var a=1,b=1,c=0,d=null,e=null,f=100,g=m,h=n;i.width=function(b){if(!arguments.length)return a;a=b;return i},i.height=function(a){if(!arguments.length)return b;b=a;return i},i.duration=function(a){if(!arguments.length)return c;c=a;return i},i.domain=function(a){if(!arguments.length)return d;d=a==null?a:d3.functor(a);return i},i.count=function(a){if(!arguments.length)return f;f=a;return i},i.x=function(a){if(!arguments.length)return g;g=a;return i},i.y=function(a){if(!arguments.length)return h;h=a;return i},i.tickFormat=function(a){if(!arguments.length)return e;e=a;return i};return i}})()
@@ -0,0 +1,92 @@
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
+ })();
@@ -0,0 +1 @@
1
+ (function(){function b(a){return/[",\n]/.test(a)?'"'+a.replace(/\"/g,'""')+'"':a}function a(a){return a.map(b).join(",")}d3.csv=function(a,b){d3.text(a,"text/csv",function(a){b(a&&d3.csv.parse(a))})},d3.csv.parse=function(a){var b;return d3.csv.parseRows(a,function(a,c){if(c){var d={},e=-1,f=b.length;while(++e<f)d[b[e]]=a[e];return d}b=a;return null})},d3.csv.parseRows=function(a,b){function j(){if(f.lastIndex>=a.length)return d;if(i){i=!1;return c}var b=f.lastIndex;if(a.charCodeAt(b)===34){var e=b;while(e++<a.length)if(a.charCodeAt(e)===34){if(a.charCodeAt(e+1)!==34)break;e++}f.lastIndex=e+2;var g=a.charCodeAt(e+1);g===13?(i=!0,a.charCodeAt(e+2)===10&&f.lastIndex++):g===10&&(i=!0);return a.substring(b+1,e).replace(/""/g,'"')}var h=f.exec(a);if(h){i=h[0].charCodeAt(0)!==44;return a.substring(b,h.index)}f.lastIndex=a.length;return a.substring(b)}var c={},d={},e=[],f=/\r\n|[,\r\n]/g,g=0,h,i;f.lastIndex=0;while((h=j())!==d){var k=[];while(h!==c&&h!==d)k.push(h),h=j();if(b&&!(k=b(k,g++)))continue;e.push(k)}return e},d3.csv.format=function(b){return b.map(a).join("\n")}})()
@@ -0,0 +1,931 @@
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
+ LineString: d3_geo_boundsLineString,
656
+ MultiLineString: d3_geo_boundsMultiLineString,
657
+ MultiPoint: d3_geo_boundsLineString,
658
+ MultiPolygon: d3_geo_boundsMultiPolygon,
659
+ Point: d3_geo_boundsPoint,
660
+ Polygon: d3_geo_boundsPolygon
661
+ };
662
+
663
+ function d3_geo_boundsFeature(o, f) {
664
+ d3_geo_bounds(o.geometry, f);
665
+ }
666
+
667
+ function d3_geo_boundsFeatureCollection(o, f) {
668
+ for (var a = o.features, i = 0, n = a.length; i < n; i++) {
669
+ d3_geo_bounds(a[i].geometry, f);
670
+ }
671
+ }
672
+
673
+ function d3_geo_boundsLineString(o, f) {
674
+ for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) {
675
+ f.apply(null, a[i]);
676
+ }
677
+ }
678
+
679
+ function d3_geo_boundsMultiLineString(o, f) {
680
+ for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) {
681
+ for (var b = a[i], j = 0, m = b.length; j < m; j++) {
682
+ f.apply(null, b[j]);
683
+ }
684
+ }
685
+ }
686
+
687
+ function d3_geo_boundsMultiPolygon(o, f) {
688
+ for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) {
689
+ for (var b = a[i][0], j = 0, m = b.length; j < m; j++) {
690
+ f.apply(null, b[j]);
691
+ }
692
+ }
693
+ }
694
+
695
+ function d3_geo_boundsPoint(o, f) {
696
+ f.apply(null, o.coordinates);
697
+ }
698
+
699
+ function d3_geo_boundsPolygon(o, f) {
700
+ for (var a = o.coordinates[0], i = 0, n = a.length; i < n; i++) {
701
+ f.apply(null, a[i]);
702
+ }
703
+ }
704
+ // TODO breakAtDateLine?
705
+
706
+ d3.geo.circle = function() {
707
+ var origin = [0, 0],
708
+ degrees = 90 - 1e-2,
709
+ radians = degrees * d3_geo_radians,
710
+ arc = d3.geo.greatArc().target(Object);
711
+
712
+ function circle() {
713
+ // TODO render a circle as a Polygon
714
+ }
715
+
716
+ function visible(point) {
717
+ return arc.distance(point) < radians;
718
+ }
719
+
720
+ circle.clip = function(d) {
721
+ arc.source(typeof origin === "function" ? origin.apply(this, arguments) : origin);
722
+ return clipType(d);
723
+ };
724
+
725
+ var clipType = d3_geo_type({
726
+
727
+ FeatureCollection: function(o) {
728
+ var features = o.features.map(clipType).filter(Object);
729
+ return features && (o = Object.create(o), o.features = features, o);
730
+ },
731
+
732
+ Feature: function(o) {
733
+ var geometry = clipType(o.geometry);
734
+ return geometry && (o = Object.create(o), o.geometry = geometry, o);
735
+ },
736
+
737
+ Point: function(o) {
738
+ return visible(o.coordinates) && o;
739
+ },
740
+
741
+ MultiPoint: function(o) {
742
+ var coordinates = o.coordinates.filter(visible);
743
+ return coordinates.length && {
744
+ type: o.type,
745
+ coordinates: coordinates
746
+ };
747
+ },
748
+
749
+ LineString: function(o) {
750
+ var coordinates = clip(o.coordinates);
751
+ return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o);
752
+ },
753
+
754
+ MultiLineString: function(o) {
755
+ var coordinates = o.coordinates.map(clip).filter(function(d) { return d.length; });
756
+ return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o);
757
+ },
758
+
759
+ Polygon: function(o) {
760
+ var coordinates = o.coordinates.map(clip);
761
+ return coordinates[0].length && (o = Object.create(o), o.coordinates = coordinates, o);
762
+ },
763
+
764
+ MultiPolygon: function(o) {
765
+ var coordinates = o.coordinates.map(function(d) { return d.map(clip); }).filter(function(d) { return d[0].length; });
766
+ return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o);
767
+ },
768
+
769
+ GeometryCollection: function(o) {
770
+ var geometries = o.geometries.map(clipType).filter(Object);
771
+ return geometries.length && (o = Object.create(o), o.geometries = geometries, o);
772
+ }
773
+
774
+ });
775
+
776
+ function clip(coordinates) {
777
+ var i = -1,
778
+ n = coordinates.length,
779
+ clipped = [],
780
+ p0,
781
+ p1,
782
+ p2,
783
+ d0,
784
+ d1;
785
+
786
+ while (++i < n) {
787
+ d1 = arc.distance(p2 = coordinates[i]);
788
+ if (d1 < radians) {
789
+ if (p1) clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1)));
790
+ clipped.push(p2);
791
+ p0 = p1 = null;
792
+ } else {
793
+ p1 = p2;
794
+ if (!p0 && clipped.length) {
795
+ clipped.push(d3_geo_greatArcInterpolate(clipped[clipped.length - 1], p1)((radians - d0) / (d1 - d0)));
796
+ p0 = p1;
797
+ }
798
+ }
799
+ d0 = d1;
800
+ }
801
+
802
+ if (p1 && clipped.length) {
803
+ d1 = arc.distance(p2 = clipped[0]);
804
+ clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1)));
805
+ }
806
+
807
+ return resample(clipped);
808
+ }
809
+
810
+ // Resample coordinates, creating great arcs between each.
811
+ function resample(coordinates) {
812
+ var i = 0,
813
+ n = coordinates.length,
814
+ j,
815
+ m,
816
+ resampled = n ? [coordinates[0]] : coordinates,
817
+ resamples,
818
+ origin = arc.source();
819
+
820
+ while (++i < n) {
821
+ resamples = arc.source(coordinates[i - 1])(coordinates[i]).coordinates;
822
+ for (j = 0, m = resamples.length; ++j < m;) resampled.push(resamples[j]);
823
+ }
824
+
825
+ arc.source(origin);
826
+ return resampled;
827
+ }
828
+
829
+ circle.origin = function(x) {
830
+ if (!arguments.length) return origin;
831
+ origin = x;
832
+ return circle;
833
+ };
834
+
835
+ circle.angle = function(x) {
836
+ if (!arguments.length) return degrees;
837
+ radians = (degrees = +x) * d3_geo_radians;
838
+ return circle;
839
+ };
840
+
841
+ // Precision is specified in degrees.
842
+ circle.precision = function(x) {
843
+ if (!arguments.length) return arc.precision();
844
+ arc.precision(x);
845
+ return circle;
846
+ };
847
+
848
+ return circle;
849
+ }
850
+ d3.geo.greatArc = function() {
851
+ var source = d3_geo_greatArcSource,
852
+ target = d3_geo_greatArcTarget,
853
+ precision = 6 * d3_geo_radians;
854
+
855
+ function greatArc() {
856
+ var a = typeof source === "function" ? source.apply(this, arguments) : source,
857
+ b = typeof target === "function" ? target.apply(this, arguments) : target,
858
+ i = d3_geo_greatArcInterpolate(a, b),
859
+ dt = precision / i.d,
860
+ t = 0,
861
+ coordinates = [a];
862
+ while ((t += dt) < 1) coordinates.push(i(t));
863
+ coordinates.push(b);
864
+ return {
865
+ type: "LineString",
866
+ coordinates: coordinates
867
+ };
868
+ }
869
+
870
+ // Length returned in radians; multiply by radius for distance.
871
+ greatArc.distance = function() {
872
+ var a = typeof source === "function" ? source.apply(this, arguments) : source,
873
+ b = typeof target === "function" ? target.apply(this, arguments) : target;
874
+ return d3_geo_greatArcInterpolate(a, b).d;
875
+ };
876
+
877
+ greatArc.source = function(x) {
878
+ if (!arguments.length) return source;
879
+ source = x;
880
+ return greatArc;
881
+ };
882
+
883
+ greatArc.target = function(x) {
884
+ if (!arguments.length) return target;
885
+ target = x;
886
+ return greatArc;
887
+ };
888
+
889
+ // Precision is specified in degrees.
890
+ greatArc.precision = function(x) {
891
+ if (!arguments.length) return precision / d3_geo_radians;
892
+ precision = x * d3_geo_radians;
893
+ return greatArc;
894
+ };
895
+
896
+ return greatArc;
897
+ };
898
+
899
+ function d3_geo_greatArcSource(d) {
900
+ return d.source;
901
+ }
902
+
903
+ function d3_geo_greatArcTarget(d) {
904
+ return d.target;
905
+ }
906
+
907
+ function d3_geo_greatArcInterpolate(a, b) {
908
+ var x0 = a[0] * d3_geo_radians, cx0 = Math.cos(x0), sx0 = Math.sin(x0),
909
+ y0 = a[1] * d3_geo_radians, cy0 = Math.cos(y0), sy0 = Math.sin(y0),
910
+ x1 = b[0] * d3_geo_radians, cx1 = Math.cos(x1), sx1 = Math.sin(x1),
911
+ y1 = b[1] * d3_geo_radians, cy1 = Math.cos(y1), sy1 = Math.sin(y1),
912
+ d = interpolate.d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)))),
913
+ sd = Math.sin(d);
914
+
915
+ // From http://williams.best.vwh.net/avform.htm#Intermediate
916
+ function interpolate(t) {
917
+ var A = Math.sin(d - (t *= d)) / sd,
918
+ B = Math.sin(t) / sd,
919
+ x = A * cy0 * cx0 + B * cy1 * cx1,
920
+ y = A * cy0 * sx0 + B * cy1 * sx1,
921
+ z = A * sy0 + B * sy1;
922
+ return [
923
+ Math.atan2(y, x) / d3_geo_radians,
924
+ Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_geo_radians
925
+ ];
926
+ }
927
+
928
+ return interpolate;
929
+ }
930
+ d3.geo.greatCircle = d3.geo.circle;
931
+ })();