|
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; |
| 1 | +function d3_geo_albers(φ0, φ1) { |
| 2 | + var sinφ0 = Math.sin(φ0), |
| 3 | + n = (sinφ0 + Math.sin(φ1)) / 2, |
| 4 | + C = 1 + sinφ0 * (2 * n - sinφ0), |
| 5 | + ρ0 = Math.sqrt(C) / n; |
| 6 | + |
| 7 | + function albers(λ, φ) { |
| 8 | + var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; |
18 | 9 | return [ |
19 | | - scale * p * Math.sin(t) + translate[0], |
20 | | - scale * (p * Math.cos(t) - p0) + translate[1] |
| 10 | + ρ * Math.sin(n * λ), |
| 11 | + ρ0 - ρ * Math.cos(n * λ) |
21 | 12 | ]; |
22 | 13 | } |
23 | 14 |
|
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); |
| 15 | + albers.invert = function(x, y) { |
| 16 | + var ρ0_y = ρ0 - y; |
30 | 17 | return [ |
31 | | - (lng0 + t / n) / d3_geo_radians, |
32 | | - Math.asin((C - p * p * n * n) / (2 * n)) / d3_geo_radians |
| 18 | + Math.atan2(x, ρ0_y) / n, |
| 19 | + Math.asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) |
33 | 20 | ]; |
34 | 21 | }; |
35 | 22 |
|
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 | | - }; |
| 23 | + return albers; |
| 24 | +} |
66 | 25 |
|
67 | | - albers.translate = function(x) { |
68 | | - if (!arguments.length) return translate; |
69 | | - translate = [+x[0], +x[1]]; |
70 | | - return albers; |
| 26 | +d3.geo.albers = function() { |
| 27 | + var p = d3_geo_doubleParallelProjection(d3_geo_albers) |
| 28 | + .rotate([98, 0]) |
| 29 | + .center([0, 38]) |
| 30 | + .parallels([29.5, 45.5]) |
| 31 | + .scale(1000); |
| 32 | + |
| 33 | + // Deprecated; use projection.rotate and projection.center instead. |
| 34 | + p.origin = function(_) { |
| 35 | + var rotate = p.rotate(), |
| 36 | + center = p.center(); |
| 37 | + if (!arguments.length) return [-rotate[0], center[1]]; |
| 38 | + return p.rotate([-_[0], rotate[1]]).center([center[0], _[1]]); |
71 | 39 | }; |
72 | 40 |
|
73 | | - return reload(); |
| 41 | + return p; |
74 | 42 | }; |
75 | 43 |
|
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]); |
| 44 | +// TODO Expose this API? Rename conicProjection? |
| 45 | +function d3_geo_doubleParallelProjection(projectAt) { |
| 46 | + var φ0 = 0, |
| 47 | + φ1 = π / 3, |
| 48 | + m = d3_geo_projectionMutator(projectAt), |
| 49 | + p = m(φ0, φ1); |
90 | 50 |
|
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; |
| 51 | + p.parallels = function(_) { |
| 52 | + if (!arguments.length) return [φ0 / π * 180, φ1 / π * 180]; |
| 53 | + return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); |
123 | 54 | }; |
124 | 55 |
|
125 | | - return albersUsa.scale(lower48.scale()); |
126 | | -}; |
| 56 | + return p; |
| 57 | +} |
0 commit comments