// Irish Grid based on code by Bill Chadwick

function IrishGraticule() {
}
IrishGraticule.prototype = new GOverlay();

IrishGraticule.prototype.initialize = function(map) {

  //save for later
  this.map_ = map;

  //array for lines 
  this.lines_ = new Array();
  
  //array for labels
  this.divs_ = new Array();

  this.drawFirst_ = true;
  this.lstnMove_ = null;
  this.lstnStart_ = null;
  this.lstnType_ = null;

      
}

IrishGraticule.prototype.remove = function() {

  	this.unDraw();

	//remove handlers we use to trigger redraw / undraw
	if(this.lstnMove_ != null)
		GEvent.removeListener(this.lstnMove_);
	if(this.lstnStart_ != null)
		GEvent.removeListener(this.lstnStart_);
	if(this.lstnType_ != null)
		GEvent.removeListener(this.lstnType_);

}

IrishGraticule.prototype.unDraw = function() {

  try{

	var i = 0;				
	for(i=0; i< this.lines_.length; i++)
		this.map_.removeOverlay(this.lines_[i]);	
			
	var div = this.map_.getPane(G_MAP_MARKER_SHADOW_PANE);
      for(i=0; i< this.divs_.length; i++)
	      div.removeChild(this.divs_[i]);

	}
  catch(e){
  }

}

IrishGraticule.prototype.copy = function() {
  return new IrishGraticule();
}

//This normally does nothing due to reentrancy problems and problems removing overlays from within an overlay
//Instead we use the moveend event to trigger a redraw, this event occurs after zoom and map type changes
IrishGraticule.prototype.redraw = function(force) {

	//but draw it the very first time
	if(this.drawFirst_)
	{
		this.safeRedraw();

  		// We use the moveend event to trigger redraw
  		var rdrw = GEvent.callback(this,this.safeRedraw );
  		this.lstnMove_ = GEvent.addListener(this.map_,"moveend",function(){rdrw ();});

  		// We use the type changed event to trigger a redraw too
  		this.lstnType_ = GEvent.addListener(this.map_,"maptypechanged",function(){rdrw ();});

		// And undraw during moves - for speed
  		var udrw = GEvent.callback(this,this.unDraw );
  		this.lstnStart_ = GEvent.addListener(this.map_,"movestart",function(){udrw ();});

		this.drawFirst_ = false;

	}
}


// Redraw the graticule based on the current projection and zoom level
IrishGraticule.prototype.safeRedraw = function() {

  //clear old
  this.unDraw();

  //best color for writing on the map
  this.color_ = this.map_.getCurrentMapType().getTextColor();
  if (this.color_ == 'white')
    this.color_ = color["lightGridLines"];
  if (this.color_ == 'black')
    this.color_ = color["darkGridLines"];

  //determine graticule interval
  var bnds = this.map_.getBounds();
  
  var l = bnds.getSouthWest().lng();
  var b = bnds.getSouthWest().lat();
  var t = bnds.getNorthEast().lat();
  var r = bnds.getNorthEast().lng();

  //sanity - limit to os grid area
  if (t < 51.0)
	return;
  if(b > 56.0)
	return;
  if(r < -12.0)
    return;  
  if(l > -2.0)
    return;
    
    

  //grid interval in km   

  var d = 100.0;
  switch (this.map_.getZoom()) // use same interval as Google's scale bar
  {
	case 5:
		d = 100.0;
		break;
	case 6:
		d = 100.0;
		break;
	case 7:
		d = 100.0;
		break;
	case 8:
		d = 100.0;
		break;
	case 9:
		d = 10.0;
		break;
	case 10:
		d = 10.0;
		break;
	case 11:
		d = 10.0;
		break;
	case 12:
		d = 1.0;
		break;
	case 13:
		d = 1.0;
		break;
	case 14:
		d = 1.0;
		break;
	case 15:
		d = 0.1;
		break;
	case 16:
		d = 0.1;
		break;
	case 17:
		d = 0.1;
		break;
	case 18:
		d = 0.01;
		break;
	case 19:
		d = 0.01;
		break;
	case 20:
		d = 0.01;
		break;
	case 21:
		d = 0.001;
		break;
      default:
		return;
  }

  var gr = IrishGraticule_enclosingOsgbRect(l,b,t,r);

//override bounds for now.
  var west = 0;//gr.bl.east / 1000.0;
  var south = 0;//gr.bl.north / 1000.0;
  var east = 400;//gr.tr.east / 1000.0;
  var north = 500;//gr.tr.north / 1000.0;

  //round iteration limits to the computed grid interval
  east = Math.ceil(east/d)*d;
  west = Math.floor(west/d)*d;
  north = Math.ceil(north/d)*d;
  south = Math.floor(south/d)*d;

  //Sanity / limit
  if (west <= 0.0)
	west = 0.0;
  if(east >= 400.0)
	east = 400.0;
  if(south < 0.0)
    south = 0.0;  
  if(north > 500.0)
    north = 500.0;

  this.lines_ = new Array();
  this.divs_ = new Array();
  
  var i=0;//count inserted lines
  var j=0;//count labels
  
  //pane/layer to write on
  var mapDiv = this.map_.getPane(G_MAP_MARKER_SHADOW_PANE);

     
  //horizontal lines
  var s = south;
  while(s<=north){
  
         var pts = new Array();	
         //under 10km grid squares draw as straight line top to bottom	 
         if(d < 10.0){
			pts[0] = this.Irish2WGS84(east,s);
			pts[1] = this.Irish2WGS84(west,s);		
		 }
         //over 10km grid squares draw as set of segments
		 else{
			var e = west;
			var q = 0;
			while(e<=east){
				pts[q] = this.Irish2WGS84(e,s);
			    q++;
				e += d;
			}
		 }
		 

		 //line
		 if(pts.length > 0)
		 {
		     this.lines_[i] = new GPolyline(pts,this.color_,1,1,{clickable:0});
		     this.map_.addOverlay(this.lines_[i]);
		     i++;
		 }
		 
		 s += d; 	
		 
		 	 			 
  }
  

  //vertical lines
  var e = west;
  while(e<=east){

         var pts2 = new Array();		 

         //under 10km grid squares draw as straight line top to bottom	 
         if(d < 10.0){
		 pts2[0] = this.Irish2WGS84(e,north);
		 pts2[1] = this.Irish2WGS84(e,south);		
		 }
         //over 10km grid squares draw as set of segments
		 else{
			var s = south;
			var q = 0;
			while(s<=north){
				pts2[q] = this.Irish2WGS84(e,s);
			    q++;
				s += d;
			}
		 }


		 //line
		 if(pts2.length > 0)
		 {
		     this.lines_[i] = new GPolyline(pts2,this.color_,1,1,{clickable:0});
		     this.map_.addOverlay(this.lines_[i]);
		     i++;
		 }

		 e += d; 		 		
		 
  }
 
}

IrishGraticule.prototype.Irish2WGS84 = function(eastKm,northKm) {

	var height = 0;

	var lat1 = GT_Math.E_N_to_Lat (eastKm*1000.0,northKm*1000.0,6377340.189,6356034.447,200000,250000,1.000035,53.50000,-8.00000);
	var lon1 = GT_Math.E_N_to_Long(eastKm*1000.0,northKm*1000.0,6377340.189,6356034.447,200000,250000,1.000035,53.50000,-8.00000);

	var x1 = GT_Math.Lat_Long_H_to_X(lat1,lon1,height,6377340.189,6356034.447);
	var y1 = GT_Math.Lat_Long_H_to_Y(lat1,lon1,height,6377340.189,6356034.447);
	var z1 = GT_Math.Lat_H_to_Z     (lat1,      height,6377340.189,6356034.447);

	var x2 = GT_Math.Helmert_X(x1,y1,z1,482.53 ,-0.214,-0.631,8.15);
	var y2 = GT_Math.Helmert_Y(x1,y1,z1,-130.596,-1.042,-0.631,8.15);
	var z2 = GT_Math.Helmert_Z(x1,y1,z1,564.557 ,-1.042,-0.214,8.15);

	var latitude = GT_Math.XYZ_to_Lat(x2,y2,z2,6378137.000,6356752.313);
	var longitude = GT_Math.XYZ_to_Long(x2,y2);

    return new GLatLng(latitude,longitude);

}

//return rect in OSGB N/E coords that encloses the given wgs84 rect
function IrishGraticule_enclosingOsgbRect(WGleft,WGbottom,WGtop,WGright){

	var blOGB = IrishGraticule_WGS842Irish(WGbottom,WGleft,0);
	var trOGB = IrishGraticule_WGS842Irish(WGtop,WGright,0);
	var brOGB = IrishGraticule_WGS842Irish(WGbottom,WGright,0);
	var tlOGB = IrishGraticule_WGS842Irish(WGtop,WGleft,0);
	
	var blEN = IrishGraticule_LLtoNE(blOGB.lat,blOGB.lon);
	var trEN = IrishGraticule_LLtoNE(trOGB.lat,trOGB.lon);
	var brEN = IrishGraticule_LLtoNE(brOGB.lat,brOGB.lon);
	var tlEN = IrishGraticule_LLtoNE(tlOGB.lat,tlOGB.lon);
	
	var e = Math.min(blEN.east,tlEN.east); 
	var w = Math.max(brEN.east,trEN.east); 
	var s = Math.min(blEN.north,brEN.north); 
	var n = Math.max(trEN.north,tlEN.north); 
	
	return new IrishGraticule_OGBRect(new IrishGraticule_OGBNorthEast(e,s),new IrishGraticule_OGBNorthEast(w,n));
}
  
function IrishGraticule_OGBLatLng(lat, lon)
{
	this.lat = lat;
	this.lon = lon;
}

//convert WGS84 Latitude and Longitude to Ordnance Survey 1936 Latitude Longitude

function IrishGraticule_WGS842Irish(WGlat, WGlon, height)
{
var uselevel2 = true;
	if (!uselevel2) {
		//fixed datum shift correction (instead of fancy hermert translation below!)
		WGlat = e-49;
		WGlon = n+23.4;
	}

    lat = GT_Math.E_N_to_Lat (WGlat,WGlon,6377340.189,6356034.447,200000,250000,1.000035,53.50000,-8.00000);
    lon = GT_Math.E_N_to_Long(WGlat,WGlon,6377340.189,6356034.447,200000,250000,1.000035,53.50000,-8.00000);

	if (uselevel2) {
		x1 = GT_Math.Lat_Long_H_to_X(lat,lon,height,6377340.189,6356034.447);
		y1 = GT_Math.Lat_Long_H_to_Y(lat,lon,height,6377340.189,6356034.447);
		z1 = GT_Math.Lat_H_to_Z     (lat,     height,6377340.189,6356034.447);

		x2 = GT_Math.Helmert_X(x1,y1,z1, 482.53 ,0.214,0.631,8.15);
		y2 = GT_Math.Helmert_Y(x1,y1,z1,-130.596,1.042,0.631,8.15);
		z2 = GT_Math.Helmert_Z(x1,y1,z1, 564.557,1.042,0.214,8.15);

		lat  = GT_Math.XYZ_to_Lat (x2,y2,z2,6378137.000,6356752.313);
		lon  = GT_Math.XYZ_to_Long(x2,y2);
	} 


return new IrishGraticule_OGBLatLng(lat, lon);

}

function IrishGraticule_OGBNorthEast(east, north)
{
	this.north = north;
	this.east = east;
}

function IrishGraticule_OGBRect(bottomLeft, topRight)
{
	this.bl = bottomLeft;
	this.tr = topRight;
}

//converts lat and lon (OSGB36) to OS northings and eastings
function IrishGraticule_LLtoNE(lat, lon)
{
var deg2rad = Math.PI / 180;
var rad2deg = 180.0 / Math.PI;

var phi = lat * deg2rad; // convert latitude to radians
var lam = lon * deg2rad; // convert longitude to radians
var a = 6377340.189; // OSGB semi-major axis
var b = 6356034.447; // OSGB semi-minor axis
var e0 = 200000; // easting of false origin
var n0 = 250000; // northing of false origin
var f0 = 1.000035; // OSGB scale factor on central meridian
var e2 = 0.00667054015; // OSGB eccentricity squared
var lam0 = -0.13962634015954636615389526147909; // OSGB false east
var phi0 = 0.93375114981696632365417456114141; // OSGB false north
var af0 = a * f0;
var bf0 = b * f0;

// easting
var slat2 = Math.sin(phi) * Math.sin(phi);
var nu = af0 / (Math.sqrt(1 - (e2 * (slat2))));
var rho = (nu * (1 - e2)) / (1 - (e2 * slat2));
var eta2 = (nu / rho) - 1;
var p = lam - lam0;
var IV = nu * Math.cos(phi);
var clat3 = Math.pow(Math.cos(phi), 3);
var tlat2 = Math.tan(phi) * Math.tan(phi);
var V = (nu / 6) * clat3 * ((nu / rho) - tlat2);
var clat5 = Math.pow(Math.cos(phi), 5);
var tlat4 = Math.pow(Math.tan(phi), 4);
var VI = (nu / 120) * clat5 * ((5 - (18 * tlat2)) + tlat4 + (14 * eta2) - (58 * tlat2 * eta2));
var east = e0 + (p * IV) + (Math.pow(p, 3) * V) + (Math.pow(p, 5) * VI);

// northing
var n = (af0 - bf0) / (af0 + bf0);
var M = GT_Math.Marc(bf0, n, phi0, phi);
var I = M + (n0);
var II = (nu / 2) * Math.sin(phi) * Math.cos(phi);
var III = ((nu / 24) * Math.sin(phi) * Math.pow(Math.cos(phi), 3)) * (5 - Math.pow(Math.tan(phi), 2) + (9 * eta2));
var IIIA = ((nu / 720) * Math.sin(phi) * clat5) * (61 - (58 * tlat2) + tlat4);
var north = I + ((p * p) * II) + (Math.pow(p, 4) * III) + (Math.pow(p, 6) * IIIA);

// make whole number values
east = Math.round(east); // round to whole number of meters
north = Math.round(north); 

return new IrishGraticule_OGBNorthEast(east, north);
}




