[76] | 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; |
---|