1 /**
  2  * The built-in projections.
  3  *
  4  * @see pv.Geo.Projection
  5  * @namespace
  6  */
  7 pv.Geo.projections = {
  8 
  9   /** @see http://en.wikipedia.org/wiki/Mercator_projection */
 10   mercator: {
 11     project: function(latlng) {
 12       return {
 13           x: latlng.lng / 180,
 14           y: latlng.lat > 85 ? 1 : latlng.lat < -85 ? -1
 15               : Math.log(Math.tan(Math.PI / 4
 16               + pv.radians(latlng.lat) / 2)) / Math.PI
 17         };
 18     },
 19     invert: function(xy) {
 20       return {
 21           lng: xy.x * 180,
 22           lat: pv.degrees(2 * Math.atan(Math.exp(xy.y * Math.PI)) - Math.PI / 2)
 23         };
 24     }
 25   },
 26 
 27   /** @see http://en.wikipedia.org/wiki/Gall-Peters_projection */
 28   "gall-peters": {
 29     project: function(latlng) {
 30       return {
 31           x: latlng.lng / 180,
 32           y: Math.sin(pv.radians(latlng.lat))
 33         };
 34     },
 35     invert: function(xy) {
 36       return {
 37           lng: xy.x * 180,
 38           lat: pv.degrees(Math.asin(xy.y))
 39         };
 40     }
 41   },
 42 
 43   /** @see http://en.wikipedia.org/wiki/Sinusoidal_projection */
 44   sinusoidal: {
 45     project: function(latlng) {
 46       return {
 47           x: pv.radians(latlng.lng) * Math.cos(pv.radians(latlng.lat)) / Math.PI,
 48           y: latlng.lat / 90
 49         };
 50     },
 51     invert: function(xy) {
 52       return {
 53           lng: pv.degrees((xy.x * Math.PI) / Math.cos(xy.y * Math.PI / 2)),
 54           lat: xy.y * 90
 55         };
 56     }
 57   },
 58 
 59   /** @see http://en.wikipedia.org/wiki/Aitoff_projection */
 60   aitoff: {
 61     project: function(latlng) {
 62       var l = pv.radians(latlng.lng),
 63           f = pv.radians(latlng.lat),
 64           a = Math.acos(Math.cos(f) * Math.cos(l / 2));
 65       return {
 66           x: 2 * (a ? (Math.cos(f) * Math.sin(l / 2) * a / Math.sin(a)) : 0) / Math.PI,
 67           y: 2 * (a ? (Math.sin(f) * a / Math.sin(a)) : 0) / Math.PI
 68         };
 69     },
 70     invert: function(xy) {
 71       var x = xy.x * Math.PI / 2,
 72           y = xy.y * Math.PI / 2;
 73       return {
 74           lng: pv.degrees(x / Math.cos(y)),
 75           lat: pv.degrees(y)
 76         };
 77     }
 78   },
 79 
 80   /** @see http://en.wikipedia.org/wiki/Hammer_projection */
 81   hammer: {
 82     project: function(latlng) {
 83       var l = pv.radians(latlng.lng),
 84           f = pv.radians(latlng.lat),
 85           c = Math.sqrt(1 + Math.cos(f) * Math.cos(l / 2));
 86       return {
 87           x: 2 * Math.SQRT2 * Math.cos(f) * Math.sin(l / 2) / c / 3,
 88           y: Math.SQRT2 * Math.sin(f) / c / 1.5
 89         };
 90     },
 91     invert: function(xy) {
 92       var x = xy.x * 3,
 93           y = xy.y * 1.5,
 94           z = Math.sqrt(1 - x * x / 16 - y * y / 4);
 95       return {
 96           lng: pv.degrees(2 * Math.atan2(z * x, 2 * (2 * z * z - 1))),
 97           lat: pv.degrees(Math.asin(z * y))
 98         };
 99     }
100   },
101 
102   /** The identity or "none" projection. */
103   identity: {
104     project: function(latlng) {
105       return {
106           x: latlng.lng / 180,
107           y: latlng.lat / 90
108         };
109     },
110     invert: function(xy) {
111       return {
112           lng: xy.x * 180,
113           lat: xy.y * 90
114         };
115     }
116   }
117 };
118