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