//--------------------------------------------------------------------------------
//	$Id: grid_utm.js,v 1.6 2010/11/20 00:11:21 wolf Exp wolf $
//--------------------------------------------------------------------------------
//	Erklärung:	http://www.netzwolf.info/kartografie/openlayers/utmgrid
//--------------------------------------------------------------------------------
//	Fragen, Wuensche, Bedenken, Anregungen?
//	<openlayers(%40)netzwolf.info>
//--------------------------------------------------------------------------------

OpenLayers.Layer.GridUTM = OpenLayers.Class (OpenLayers.Layer.Vector, {

	initialize: function (name, options){
		OpenLayers.Layer.Vector.prototype.initialize.apply(this, [name, options]);
	},

	gridSizeText: null,

	gridSizeDiv: null,

	zone: null,

	//---------------------------------------------------------
	//	Find matching grid unit or return null
	//---------------------------------------------------------

	getGridUnit: function (distance) {
		for (var i=0; i<this.gridUnits.length; i++) {
			if (distance<this.gridUnits[i]) return this.gridUnits[i];
		}
		return null;
	},

	gridUnits: [50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000],

	gridPixelDistance: 100,

	//---------------------------------------------------------
	//	Format gridsize
	//---------------------------------------------------------

	formatGridSize: function (m) {
		if (m>=1000 && m%1000==0) return (m/1000)+"km";
		return m+"m";
	},

	//---------------------------------------------------------
	//	Set graphic attributes for grid
	//---------------------------------------------------------

	styleMap: new OpenLayers.StyleMap( {
		strokeColor: "#000000",
		strokeWidth: 1,
		strokeOpacity: 0.8
	}),

	//---------------------------------------------------------
	//	Draw grid on move or zoom
	//---------------------------------------------------------

	moveTo: function (bounds, zoomChanged, dragging) {

		//---------------------------------------------------------
		//	but not while dragging
		//---------------------------------------------------------

		if (dragging) return;

		//---------------------------------------------------------
		//	Remove old grid
		//---------------------------------------------------------

		this.destroyFeatures();

		//---------------------------------------------------------
		//	Transform center and border to geogr. Coordinates
		//---------------------------------------------------------

		var mapCenter = this.map.getCenter().clone().
			transform(this.map.getProjectionObject(), this.map.displayProjection);

		var mapBounds = bounds.clone().
			transform(this.map.getProjectionObject(), this.map.displayProjection);

		//----------------------------------------------------------
		//	Number of UTM-Zones in viewport
		//----------------------------------------------------------

		var nzones = (this.lonToZone(mapBounds.right)-this.lonToZone(mapBounds.left)+60)%60 + 1;

		//----------------------------------------------------------
		//	Set zone based on center of map
		//----------------------------------------------------------

		if (nzones >= 3) {
			this.zone = null;
		} else if (nzones==1 || !this.zone) {
			this.zone = this.lonToZone (mapCenter.lon);
		}

		//---------------------------------------------------------
		//	UTM-Coordinates of corners
		//---------------------------------------------------------

		var ol = this.lonLatToUTM (mapBounds.left , mapBounds.top   );
		var or = this.lonLatToUTM (mapBounds.right, mapBounds.top   );
		var ul = this.lonLatToUTM (mapBounds.left , mapBounds.bottom);
		var ur = this.lonLatToUTM (mapBounds.right, mapBounds.bottom);

		//---------------------------------------------------------
		//	Grid unit
		//---------------------------------------------------------

		var unit = ur && ul ?
			this.getGridUnit ((ur.x-ul.x) / this.map.getSize().w * this.gridPixelDistance) : null;

		//---------------------------------------------------------
		//	Grid size display object
		//	(TODO: create a OpenLayers.Control-Object)
		//---------------------------------------------------------

		if (this.gridSizeText && !this.gridSizeDiv) {
			this.gridSizeDiv=OpenLayers.Util.createDiv(this.id);
			this.gridSizeDiv.className='olControlGridUTM';
			this.gridSizeDiv.style.zIndex=map.Z_INDEX_BASE['Control']+ map.controls.length;
			this.gridSizeDiv.setAttribute("unselectable","on");
			this.map.viewPortDiv.appendChild (this.gridSizeDiv);

		}

		//---------------------------------------------------------
		//	Hide grid size (if configured)
		//---------------------------------------------------------

		if (this.gridSizeDiv) this.gridSizeDiv.style.display='none';

		//---------------------------------------------------------
		//	Create new grid
		//---------------------------------------------------------

		if (this.zone && unit) {

			//---------------------------------------------------------
			//	Compute grid
			//---------------------------------------------------------

			var x1 = Math.floor (Math.min(ol.x, ul.x) / unit) * unit;
			var x2 = Math.ceil  (Math.max(or.x, ur.x) / unit) * unit;
			var y1 = Math.floor (Math.min(ul.y, ur.y) / unit) * unit;
			var y2 = Math.ceil  (Math.max(ol.y, or.y) / unit) * unit;

			var features = [];

			//---------------------------------------------------------
			//	Vertical lines
			//---------------------------------------------------------

			for (var x=x1; x<=x2; x+= unit) {
				var xu = (x - ul.x) / (ur.x-ul.x) * (bounds.right-bounds.left) +bounds.left;
				var xo = (x - ol.x) / (or.x-ol.x) * (bounds.right-bounds.left) +bounds.left;
				features.push (
					new OpenLayers.Feature.Vector ( new OpenLayers.Geometry.LineString( [
						new OpenLayers.Geometry.Point (xu, bounds.bottom),
						new OpenLayers.Geometry.Point (xo, bounds.top   )
					])));
			}

			//---------------------------------------------------------
			//	Horizontal lines
			//---------------------------------------------------------

			for (var y=y1; y<=y2; y+=unit) {
				var yl = (y - ul.y) / (ol.y-ul.y) * (bounds.top-bounds.bottom) +bounds.bottom;
				var yr = (y - ur.y) / (or.y-ur.y) * (bounds.top-bounds.bottom) +bounds.bottom;
				features.push (
					new OpenLayers.Feature.Vector ( new OpenLayers.Geometry.LineString( [
						new OpenLayers.Geometry.Point (bounds.left,  yl),
						new OpenLayers.Geometry.Point (bounds.right, yr)
					])));
			}

			//---------------------------------------------------------
			//	Add grid lines to vector layer
			//---------------------------------------------------------

			this.addFeatures(features);

			//---------------------------------------------------------
			//	Display grid size
			//---------------------------------------------------------

			if (this.gridSizeDiv) {
				this.gridSizeDiv.innerHTML = OpenLayers.String.format(this.gridSizeText,
					{grid: this.formatGridSize(unit)});
				this.gridSizeDiv.style.display=null;
			}
		}

		//---------------------------------------------------------
		//	Superclass
		//---------------------------------------------------------

		OpenLayers.Layer.Vector.prototype.moveTo.apply(this,arguments);
	},

	//---------------------------------------------------------
	//	Determine UTM-Zone from WGS84-Longitude
	//---------------------------------------------------------

	lonToZone: function (lon) {
		lon -= Math.floor(lon/360+0.5)*360;
		return Math.floor (lon/6.0) + 31;
	},

	//---------------------------------------------------------
	//	Convert WGS84 Longitude and Lattitude to UTM
	//		using Zone from this.zone
	//---------------------------------------------------------

	lonLatToUTM: function (lon, lat) {

		//---------------------------------------------------------
		//	Sanitize and normalize values
		//---------------------------------------------------------

		if (!this.zone) return null;
		if (lat>=+90) return null;
		if (lat<=-90) return null;
		if (lat > 84.0) lat = +84;
		if (lat <-80.0) lat = -80;
		lon -= Math.floor(lon/360+0.5)*360;

		//---------------------------------------------------------
		//	Get center meridian
		//---------------------------------------------------------

		var meridian = this.zone*6 - 183;

		//---------------------------------------------------------
		//	Longitude based on middle meridian
		//	Transform from degrees to radians
		//---------------------------------------------------------

		var rho = 180.0/3.14159265358979;
		var phi = lat/rho;
		var lbd = (lon-meridian)/rho;

		//---------------------------------------------------------
		//	Auxiliary values based on the WGS84 ellipsoid
		//---------------------------------------------------------

		var sin = Math.sin (phi);
		var cos = Math.cos (phi);
		var tan = Math.tan (phi);
		var c2  = cos*cos;
		var t2  = tan*tan;
		var l2  = lbd*lbd;
		var eta = 0.00673949674227643 * c2;
		var Vq  = 1 + eta;
		var V   = Math.sqrt(Vq);
		var N   = 6399593.62575849/V;

		//---------------------------------------------------------
		//	Conforming transformation to northing and easting
		//	.oO( you are not expected to understand this :-)
		//---------------------------------------------------------

			var nv = 6367449.14582342 * phi + sin * cos *
				(((0.00403587903241357 * c2 - 0.709736085843171)
				* c2 + 135.398511127892) * c2 - 32144.4799350778)
				+ N * (0.5 * tan * c2 * l2 * (1 + 1/12 * (5+9*eta-t2)*c2*l2));;
			var ev = N * cos * lbd * (1+ 1/6 * c2 * l2 * (Vq - t2 +0.05 * (5-18*t2+t2*t2) * c2 * l2));

		//---------------------------------------------------------
		//	return northing and easting
		//---------------------------------------------------------

		return {
			y: Math.round (0.9996*nv),
			x: Math.round (0.9996*ev)
		};

   	},

	CLASS_NAME: "OpenLayers.Layer.GridUTM"
});

//--------------------------------------------------------------------------------
//	$Id: grid_utm.js,v 1.6 2010/11/20 00:11:21 wolf Exp wolf $
//--------------------------------------------------------------------------------

