
/** The TMarker object constructor. */
function TMarker ( lng, lat, linkUrl, markerUrl, yoffset, xoffset ){
	// member variables
	this.lng = lng;
	this.lat = lat;
	this.linkUrl = linkUrl;
	this.markerUrl = markerUrl;
	this.yoffset = yoffset;
	this.xoffset = xoffset;
}

/** The TMap object constructor. */
function TMap ( image_width , image_height, extent_top, extent_bottom, extent_left, extent_right, region_hash, mapElemName,longMin, longMax ) {
	// member variables

	this.image_width = image_width;
	this.image_height = image_height;
	this.extent_top = extent_top;
	this.extent_bottom = extent_bottom;
	this.extent_left = extent_left;
	this.extent_right = extent_right;
	this.map_width_km = Math.round((this.longitudeDegreesToMerc(longMax) - this.longitudeDegreesToMerc(longMin))/1000 / 2.8);
	this.pixelwidth = Math.round((extent_right - extent_left) / image_width, 0);
	this.pixelheight = Math.round((extent_top - extent_bottom) / image_height, 0);	
	this.region_hash = region_hash
	this.mapElemName = mapElemName;
	this.markers = Array();
}

TMap.prototype.showimage  = function (place) {  
	if(place == undefined)
	{
		document.getElementById("mapimg").src = "/maps/"+this.region_hash+"/map.png";
	}
	else
	{
		document.getElementById("mapimg").src = "/maps/"+this.region_hash+"/"+place+".gif";
	}
};

TMap.prototype.shownoimage = function () {
	document.getElementById("mapimg").src = "/maps/"+this.region_hash+"/map.png";
};


//TMap.prototype.mapClick  = function (region) {
//	document.location = "/search"+region+".html";
//};

TMap.prototype.addMarker  = function (lat, lng, linkUrl, markerUrl, yoffset, xoffset) {
	var marker = new TMarker ( lng, lat, linkUrl, markerUrl, yoffset, xoffset );
	this.markers.push(marker);
};

TMap.prototype.plotMarker  = function (lat, lng, linkUrl, markerUrl, yoffset, xoffset) {
	var mmlat = (this.latitudeDegreesToMerc(lat));
	var mmlng = (this.longitudeDegreesToMerc(lng));

	//alert(this.extent_bottom + " " + mmlat + " " + this.extent_top);
	//alert(this.extent_lft + " " + mmlng + " " + this.extent_right);

	if ((mmlat <= this.extent_top) && (mmlat >= this.extent_bottom) && 
		(mmlng >= this.extent_left) && (mmlng <= this.extent_right))
	{
		var mapdiv = document.getElementById(this.mapElemName);
		var newdiv = document.createElement('div');
		//var divIdName = 'my'+num+'Div';
		//newdiv.setAttribute('id',divIdName);
		newdiv.style.position = 'absolute';
		//alert('url("'+markerUrl+'")');
		//newdiv.style.background = 'url("'+markerUrl+'")';
		newdiv.onclick = function(){ document.location=linkUrl; }
		newdiv.innerHTML = "<img src=\""+markerUrl+"\">";
		newdiv.style.top = (this.pixelPointYFromUnits(mmlat)+yoffset) + 'px';
		newdiv.style.left = (this.pixelPointXFromUnits(mmlng)+xoffset) + 'px';
		mapdiv.appendChild(newdiv);
	}
};

TMap.prototype.draw  = function () {
	this.markers.sort(this.sortByLat);
	for (i = 0; i < this.markers.length; i++)
	{
		var marker = this.markers[i];
		this.plotMarker(marker.lat, marker.lng, marker.linkUrl, marker.markerUrl, marker.yoffset, marker.xoffset);
	}
};

TMap.prototype.plotTown = function(name, lat, lng) {

	this.plotMarker(lat, lng, "", "/img/1.png",0,0);
	
	var jg = new jsGraphics("loc_box");
	
	var mmlat = (this.latitudeDegreesToMerc(lat));
	var mmlng = (this.longitudeDegreesToMerc(lng));

	var x = this.pixelPointXFromUnits(mmlng);
	var y = this.pixelPointYFromUnits(mmlat);

	jg.setColor("#333");
	jg.setFont("arial","9pt", Font.BOLD);
	jg.drawString(name,x,y);

	jg.paint();  

};

TMap.prototype.showRadius = function(radius_km, location, lat, lng) {

	var jg = new jsGraphics("mapdiv");
	jg.setColor("red");
	jg.setStroke(2);

	var mmlat = (this.latitudeDegreesToMerc(lat));
	var mmlng = (this.longitudeDegreesToMerc(lng));
	var radius_pixels = Math.round((this.image_width * (radius_km / this.map_width_km))/2); 

	var x = this.pixelPointXFromUnits(mmlng);
	var y = this.pixelPointYFromUnits(mmlat);

	jg.drawLine(x-radius_pixels, y, x-radius_pixels-5,y);	//Left
	jg.drawLine(x+radius_pixels, y, x+(radius_pixels)+5,y);	//Left
	jg.drawLine(x, y-radius_pixels, x,y-radius_pixels-5);	//Top
	jg.drawLine(x, y+radius_pixels, x,y+radius_pixels+5);	//Bottom
	
	jg.drawEllipse(x - radius_pixels, y - radius_pixels, radius_pixels*2, radius_pixels*2);  
	jg.setColor("#333");
	jg.setFont("arial","9pt", Font.BOLD);
	jg.drawString(location,x,y);

	jg.paint();  

};

TMap.prototype.sortByLat = function (a, b)
{
	return (b.lat - a.lat);
}

/* PIXEL CONVERSION FUNCTIONS */
TMap.prototype.pixelPointXFromUnits = function (lng) {
	htmlX = (lng - this.extent_left) / this.pixelwidth;
	return Math.round(htmlX,0);
};

TMap.prototype.pixelPointYFromUnits = function (lat) {
	htmlY = (lat - this.extent_bottom) / this.pixelheight;
	y = this.image_height - Math.round(htmlY,0);
	return y;
};	
	
TMap.prototype.longToUnits = function (lng) {
	var x = this.pixelPointXFromUnits(this.longitudeDegreesToMerc(lng));
	return x;
};

TMap.prototype.latToUnits = function (lat) {
	var y = this.pixelPointYFromUnits(this.latitudeDegreesToMerc(lat));
	return y;
};

TMap.prototype.longitudeDegreesToMerc = function (lng) {
	return (lng * 111319.4908);
};

TMap.prototype.latitudeDegreesToMerc  = function (latitudeDegrees) {
	var lat = latitudeDegrees * (Math.PI / 180);
	var e = 0.0818191913108718138;
	var a = 6378137;
	var eMULTsinlat = e * Math.sin(lat);
	var y = a * Math.log ( Math.tan ((Math.PI/4) + (lat/2)) * Math.pow( ( (1 - eMULTsinlat) / (1 + eMULTsinlat) ) , (e/2) )  );
	return y;
};

function clientSideInclude(id, url) {
	var req = false;
	// For Safari, Firefox, and other non-MS browsers
	if (window.XMLHttpRequest) {
		try {
			req = new XMLHttpRequest();
		} catch (e) {
			req = false;
		}
	} 
	else if (window.ActiveXObject) {
		// For Internet Explorer on Windows
		try { req = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {
		try { req = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {
			  req = false; } }
	}
	var element = document.getElementById(id);
	if (!element) {
		alert("Bad id " + id + "passed to clientSideInclude. You need a an element with this id in your page.");
		return;
	}
	if (req) {
		// Synchronous request, wait till we have it all
		req.open('GET', url, false);
		req.send(null);
		element.innerHTML = req.responseText;
	} else {
		element.innerHTML = "Sorry, your browser does not support XMLHTTPRequest objects. We recommend the Firefox browser.";
	}
}






function AreaClickHandler(e) {
	if (tmap != null)
		tmap.mapClick(this.mapUrl); 
}

function AreaMouseOverHandler(e) {
	if (tmap != null)
		tmap.showimage(this.mapHash);
}

function AreaMouseOutHandler(e) {
	if (tmap != null)
		tmap.shownoimage();
}

function setAreaHandlers()
{
	var maparea =  document.getElementById("mapareas");
	
	if (maparea != null)
	{
		areas =  maparea.childNodes;

		for ( i = 0; i < areas.length; i++)
		{
			var item = areas.item(i);
			
			if ( item.id != null && item != "" )
			{
				var strings = item.id.split("_");
				item.mapUrl = item.href+".html";
				item.mapHash = strings[1];
				item.ClickHandler = AreaClickHandler; 
				item.MouseOverHandler = AreaMouseOverHandler; 
				item.MouseOutHandler = AreaMouseOutHandler; 
				XBrowserAddHandler(item,'click',"ClickHandler");
				XBrowserAddHandler(item,'mouseover',"MouseOverHandler");
				XBrowserAddHandler(item,'mouseout',"MouseOutHandler");
			}
		}
	}
}



/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
/*  Latitude/longitude spherical geodesy formulae & scripts (c) Chris Veness 2002-2009            */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

/*
 * Use Haversine formula to Calculate distance (in km) between two points specified by 
 * latitude/longitude (in numeric degrees)
 *
 * example usage from form:
 *   result.value = LatLon.distHaversine(lat1.value.parseDeg(), long1.value.parseDeg(), 
 *                                       lat2.value.parseDeg(), long2.value.parseDeg());
 * where lat1, long1, lat2, long2, and result are form fields
 */
 
function distance(lat1, lon1, lat2, lon2, unit) {
	var radlat1 = Math.PI * lat1/180
	var radlat2 = Math.PI * lat2/180
	var radlon1 = Math.PI * lon1/180
	var radlon2 = Math.PI * lon2/180
	var theta = lon1-lon2
	var radtheta = Math.PI * theta/180
	var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
	dist = Math.acos(dist)
	dist = dist * 180/Math.PI
	dist = dist * 60 * 1.1515
	if (unit=="K") { dist = dist * 1.609344 }
	if (unit=="N") { dist = dist * 0.8684 }
	return dist
}     

LatLon.distHaversine = function(lat1, lon1, lat2, lon2) {
  var R = 6371; // earth's mean radius in km
  var dLat = (lat2-lat1).toRad();
  var dLon = (lon2-lon1).toRad();
  lat1 = lat1.toRad(), lat2 = lat2.toRad();

  var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
          Math.cos(lat1) * Math.cos(lat2) * 
          Math.sin(dLon/2) * Math.sin(dLon/2);
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  var d = R * c;
  return d; // * 1.07;
}



/*
 * construct a LatLon object: arguments in numeric degrees
 *
 * note all LatLong methods expect & return numeric degrees (for lat/long & for bearings)
 */
function LatLon(lat, lon) {
  this.lat = lat;
  this.lon = lon;
}


/*
 * represent point {lat, lon} in standard representation
 */
LatLon.prototype.toString = function() {
  return this.lat.toLat() + ', ' + this.lon.toLon();
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

// extend String object with method for parsing degrees or lat/long values to numeric degrees
//
// this is very flexible on formats, allowing signed decimal degrees, or deg-min-sec suffixed by 
// compass direction (NSEW). A variety of separators are accepted (eg 3º 37' 09"W) or fixed-width 
// format without separators (eg 0033709W). Seconds and minutes may be omitted. (Minimal validation 
// is done).

String.prototype.parseDeg = function() {
  if (!isNaN(this)) return Number(this);                 // signed decimal degrees without NSEW

  var degLL = this.replace(/^-/,'').replace(/[NSEW]/i,'');  // strip off any sign or compass dir'n
  var dms = degLL.split(/[^0-9.]+/);                     // split out separate d/m/s
  for (var i in dms) if (dms[i]=='') dms.splice(i,1);    // remove empty elements (see note below)
  switch (dms.length) {                                  // convert to decimal degrees...
    case 3:                                              // interpret 3-part result as d/m/s
      var deg = dms[0]/1 + dms[1]/60 + dms[2]/3600; break;
    case 2:                                              // interpret 2-part result as d/m
      var deg = dms[0]/1 + dms[1]/60; break;
    case 1:                                              // decimal or non-separated dddmmss
      if (/[NS]/i.test(this)) degLL = '0' + degLL;       // - normalise N/S to 3-digit degrees
      var deg = dms[0].slice(0,3)/1 + dms[0].slice(3,5)/60 + dms[0].slice(5)/3600; break;
    default: return NaN;
  }
  if (/^-/.test(this) || /[WS]/i.test(this)) deg = -deg; // take '-', west and south as -ve
  return deg;
}
// note: whitespace at start/end will split() into empty elements (except in IE)


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

// extend Number object with methods for converting degrees/radians

Number.prototype.toRad = function() {  // convert degrees to radians
  return this * Math.PI / 180;
}

Number.prototype.toDeg = function() {  // convert radians to degrees (signed)
  return this * 180 / Math.PI;
}

Number.prototype.toBrng = function() {  // convert radians to degrees (as bearing: 0...360)
  return (this.toDeg()+360) % 360;
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */

// extend Number object with methods for presenting bearings & lat/longs

Number.prototype.toDMS = function() {  // convert numeric degrees to deg/min/sec
  var d = Math.abs(this);  // (unsigned result ready for appending compass dir'n)
  d += 1/7200;  // add ½ second for rounding
  var deg = Math.floor(d);
  var min = Math.floor((d-deg)*60);
  var sec = Math.floor((d-deg-min/60)*3600);
  // add leading zeros if required
  if (deg<100) deg = '0' + deg; if (deg<10) deg = '0' + deg;
  if (min<10) min = '0' + min;
  if (sec<10) sec = '0' + sec;
  return deg + '\u00B0' + min + '\u2032' + sec + '\u2033';
}

Number.prototype.toLat = function() {  // convert numeric degrees to deg/min/sec latitude
  return this.toDMS().slice(1) + (this<0 ? 'S' : 'N');  // knock off initial '0' for lat!
}

Number.prototype.toLon = function() {  // convert numeric degrees to deg/min/sec longitude
  return this.toDMS() + (this>0 ? 'E' : 'W');
}

Number.prototype.toPrecision = function(fig) {  // override toPrecision method with one which displays 
  if (this == 0) return 0;                      // trailing zeros in place of exponential notation
  var scale = Math.ceil(Math.log(this)*Math.LOG10E);
  var mult = Math.pow(10, fig-scale);
  return Math.round(this*mult)/mult;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */


