1 | // Derived from Tom Carden's Albers implementation for Protovis. |
---|
2 | // http://gist.github.com/476238 |
---|
3 | // http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html |
---|
4 | |
---|
5 | d3.geo.albers = function() { |
---|
6 | var origin = [-98, 38], |
---|
7 | parallels = [29.5, 45.5], |
---|
8 | scale = 1000, |
---|
9 | translate = [480, 250], |
---|
10 | lng0, // d3_radians * origin[0] |
---|
11 | n, |
---|
12 | C, |
---|
13 | p0; |
---|
14 | |
---|
15 | function albers(coordinates) { |
---|
16 | var t = n * (d3_radians * coordinates[0] - lng0), |
---|
17 | p = Math.sqrt(C - 2 * n * Math.sin(d3_radians * coordinates[1])) / n; |
---|
18 | return [ |
---|
19 | scale * p * Math.sin(t) + translate[0], |
---|
20 | scale * (p * Math.cos(t) - p0) + translate[1] |
---|
21 | ]; |
---|
22 | } |
---|
23 | |
---|
24 | function reload() { |
---|
25 | var phi1 = d3_radians * parallels[0], |
---|
26 | phi2 = d3_radians * parallels[1], |
---|
27 | lat0 = d3_radians * origin[1], |
---|
28 | s = Math.sin(phi1), |
---|
29 | c = Math.cos(phi1); |
---|
30 | lng0 = d3_radians * origin[0]; |
---|
31 | n = .5 * (s + Math.sin(phi2)); |
---|
32 | C = c * c + 2 * n * s; |
---|
33 | p0 = Math.sqrt(C - 2 * n * Math.sin(lat0)) / n; |
---|
34 | return albers; |
---|
35 | } |
---|
36 | |
---|
37 | albers.origin = function(x) { |
---|
38 | if (!arguments.length) return origin; |
---|
39 | origin = [+x[0], +x[1]]; |
---|
40 | return reload(); |
---|
41 | }; |
---|
42 | |
---|
43 | albers.parallels = function(x) { |
---|
44 | if (!arguments.length) return parallels; |
---|
45 | parallels = [+x[0], +x[1]]; |
---|
46 | return reload(); |
---|
47 | }; |
---|
48 | |
---|
49 | albers.scale = function(x) { |
---|
50 | if (!arguments.length) return scale; |
---|
51 | scale = +x; |
---|
52 | return albers; |
---|
53 | }; |
---|
54 | |
---|
55 | albers.translate = function(x) { |
---|
56 | if (!arguments.length) return translate; |
---|
57 | translate = [+x[0], +x[1]]; |
---|
58 | return albers; |
---|
59 | }; |
---|
60 | |
---|
61 | return reload(); |
---|
62 | }; |
---|
63 | |
---|
64 | // A composite projection for the United States, 960x500. The set of standard |
---|
65 | // parallels for each region comes from USGS, which is published here: |
---|
66 | // http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers |
---|
67 | // TODO allow the composite projection to be rescaled? |
---|
68 | d3.geo.albersUsa = function() { |
---|
69 | var lower48 = d3.geo.albers(); |
---|
70 | |
---|
71 | var alaska = d3.geo.albers() |
---|
72 | .origin([-160, 60]) |
---|
73 | .parallels([55, 65]); |
---|
74 | |
---|
75 | var hawaii = d3.geo.albers() |
---|
76 | .origin([-160, 20]) |
---|
77 | .parallels([8, 18]); |
---|
78 | |
---|
79 | var puertoRico = d3.geo.albers() |
---|
80 | .origin([-60, 10]) |
---|
81 | .parallels([8, 18]); |
---|
82 | |
---|
83 | function albersUsa(coordinates) { |
---|
84 | var lon = coordinates[0], |
---|
85 | lat = coordinates[1]; |
---|
86 | return (lat < 25 |
---|
87 | ? (lon < -100 ? hawaii : puertoRico) |
---|
88 | : (lat > 50 ? alaska : lower48))(coordinates); |
---|
89 | } |
---|
90 | |
---|
91 | albersUsa.scale = function(x) { |
---|
92 | if (!arguments.length) return lower48.scale(); |
---|
93 | lower48.scale(x); |
---|
94 | alaska.scale(x * .6); |
---|
95 | hawaii.scale(x); |
---|
96 | puertoRico.scale(x * 1.5); |
---|
97 | return albersUsa.translate(lower48.translate()); |
---|
98 | }; |
---|
99 | |
---|
100 | albersUsa.translate = function(x) { |
---|
101 | if (!arguments.length) return lower48.translate(); |
---|
102 | var dz = lower48.scale() / 1000, |
---|
103 | dx = x[0], |
---|
104 | dy = x[1]; |
---|
105 | lower48.translate(x); |
---|
106 | alaska.translate([dx - 400 * dz, dy + 170 * dz]); |
---|
107 | hawaii.translate([dx - 190 * dz, dy + 200 * dz]); |
---|
108 | puertoRico.translate([dx + 580 * dz, dy + 430 * dz]); |
---|
109 | return albersUsa; |
---|
110 | }; |
---|
111 | |
---|
112 | return albersUsa.scale(lower48.scale()); |
---|
113 | }; |
---|
114 | |
---|
115 | var d3_radians = Math.PI / 180; |
---|