NORTH = 1.0;
SOUTH = -1.0;

function PolarStereographicProjection( zoomlevels, type ) {
  var me = this;
  me.zoomlevels = zoomlevels;
  me.zvector = type;
  // This is all kinds of fun.  Thanks to Mike Williams in the Google
  // Maps API Google Group for coming up with this remarkably hideous
  // hack.  This works around the fact that the Google Maps API
  // computes whether polyline elements are currently visible based 
  // on a computation in lat/lon space that breaks horribly for
  // non-standard projections.  It works by faking out the Maps API 
  // when it's doing those computations, which are identifiable 
  // because they always take place at zoom level 17.  (Also fun.)
  // The rest of the hack is down in the fromPixelToLatLng function.
  // Polygons are still broken.
  me.magic = true;
}

PolarStereographicProjection.prototype = new GProjection();

PolarStereographicProjection.prototype.fromLatLngToPixel = function(latlng, zoom) {
  var lat = Math.PI/180 * latlng.lat();
  var lon = Math.PI/180 * latlng.lng();
  var s1 = Math.cos(lat)/(1.0 + this.zvector * Math.sin(lat));
  var s2 = 256 / 2 * Math.pow(2,zoom);
  var x = ( Math.sin(lon) * s1 + 1 ) * s2;
  var y = ( this.zvector * Math.cos(lon) * s1 + 1 ) * s2;
  return new GPoint(x,y);
};

PolarStereographicProjection.prototype.fromPixelToLatLng = function(pixel, zoom, unbounded) {
  if (zoom == 17) {
    this.magic = ! this.magic;
    if( this.magic ) {
      return new GLatLng(90,180);
    } else {
      return new GLatLng(-90,-180);
    }
  }
  var s2 = 256 / 2 * Math.pow(2,zoom);
  var x = pixel.x / s2 - 1;
  var y = pixel.y / s2 - 1;
  if ( x == 0 && y == 0 ) return new GLatLng(90,0);
  var z = this.zvector * Math.min( 2.0 / ( x*x + y*y + 1 ) - 1.0, 1.0 );
  var lon = 180/Math.PI * Math.atan2( x, this.zvector * y );
  var lat = 180/Math.PI * Math.asin( z );
  return new GLatLng(lat,lon);
};

PolarStereographicProjection.prototype.tileCheckRange = function(tile, zoom, tilesize) {
  if( zoom > this.zoomlevels ) return false;
  else return true;
};

PolarStereographicProjection.prototype.getWrapWidth = function(zoom) {
  // Is there a better cross-platform way to represent Infinity here?
  return 1E+100 * Math.pow(2,zoom);
};

function TMSBaseLayer( baseurl, levels ) {
  var layer = new GTileLayer(null, 0, levels);
  layer.isPng = function() { return false };
  layer.getTileUrl = function(tile, zoom) {
    var y = Math.pow(2, zoom)-tile.y-1;
    var url = baseurl + zoom + "/" + tile.x + "/" + y + ".jpg";
    return url;
  };
  return layer;
}

var NASA_MOON_VISIBLE_MAP = new GMapType( [TMSBaseLayer("http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw/",9)],
					  new GMercatorProjection(18), "Visible" );

var NASA_MOON_VISIBLE_MAP_NPOLAR = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/moon/visible_north/",8)],
						 new PolarStereographicProjection(8,NORTH),
						 "North Polar Visible" );

var NASA_MOON_VISIBLE_MAP_SPOLAR = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/moon/visible_south/",8)],
						 new PolarStereographicProjection(8,SOUTH),
						 "South Polar Visible" );

var NASA_MOON_ELEVATION_MAP = new GMapType( [TMSBaseLayer("http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/terrain/",7)],
					    new GMercatorProjection(18), "Elevation" );

var NASA_MOON_ELEVATION_MAP_NPOLAR = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/moon/terrain_north/",6)],
						   new PolarStereographicProjection(8,NORTH),
						   "North Polar Elevation" );

var NASA_MOON_ELEVATION_MAP_SPOLAR = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/moon/terrain_south/",6)],
						   new PolarStereographicProjection(8,SOUTH),
						   "South Polar Elevation" );

var NASA_MARS_VISIBLE_MAP = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/mars/visible/",9)],
					  new GMercatorProjection(18), "Visible" );

var NASA_MARS_VISIBLE_MAP_NPOLAR = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/mars/visible_north/",8)],
						 new PolarStereographicProjection(8,NORTH),
						 "North Polar Visible" );

var NASA_MARS_VISIBLE_MAP_SPOLAR = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/mars/visible_south/",8)],
						 new PolarStereographicProjection(8,SOUTH),
						 "South Polar Visible" );

var NASA_MARS_ELEVATION_MAP = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/mars/terrain/",8)],
					    new GMercatorProjection(18), "Elevation" );

var NASA_MARS_ELEVATION_MAP_NPOLAR = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/mars/terrain_north/",7)],
						   new PolarStereographicProjection(8,NORTH),
						   "North Polar Elevation" );

var NASA_MARS_ELEVATION_MAP_SPOLAR = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/mars/terrain_south/",7)],
						   new PolarStereographicProjection(8,SOUTH),
						   "South Polar Elevation" );
var NASA_MARS_DAYIR_MAP = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/mars/themis_dayir_100m/",10)],
                                          new GMercatorProjection(18), "Day IR" );
var NASA_MARS_NIGHTIR_MAP = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/mars/themis_nightir_100m/",10)],
                                          new GMercatorProjection(18), "Night IR" );
var NASA_MARS_TESTIR_MAP = new GMapType( [TMSBaseLayer("http://byss.arc.nasa.gov/maps/mars/themis_nightir_withoutpoles/",10)],
                                          new GMercatorProjection(18), "Night IR w/o poles" );

NASA_MOON_MAP_TYPES = [NASA_MOON_VISIBLE_MAP,NASA_MOON_ELEVATION_MAP];
NASA_MOON_MAP_TYPES_NPOLAR = [NASA_MOON_VISIBLE_MAP_NPOLAR,NASA_MOON_ELEVATION_MAP_NPOLAR];
NASA_MOON_MAP_TYPES_SPOLAR = [NASA_MOON_VISIBLE_MAP_SPOLAR,NASA_MOON_ELEVATION_MAP_SPOLAR];
NASA_MARS_MAP_TYPES = [NASA_MARS_VISIBLE_MAP,NASA_MARS_ELEVATION_MAP,NASA_MARS_DAYIR_MAP,NASA_MARS_NIGHTIR_MAP,NASA_MARS_TESTIR_MAP];
NASA_MARS_MAP_TYPES_NPOLAR = [NASA_MARS_VISIBLE_MAP_NPOLAR,NASA_MARS_ELEVATION_MAP_NPOLAR];
NASA_MARS_MAP_TYPES_SPOLAR = [NASA_MARS_VISIBLE_MAP_SPOLAR,NASA_MARS_ELEVATION_MAP_SPOLAR];
