1 /***************************************
  2  * Copyright 2011, 2012 GlobWeb contributors.
  3  *
  4  * This file is part of GlobWeb.
  5  *
  6  * GlobWeb is free software: you can redistribute it and/or modify
  7  * it under the terms of the GNU Lesser General Public License as published by
  8  * the Free Software Foundation, version 3 of the License, or
  9  * (at your option) any later version.
 10  *
 11  * GlobWeb is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14  * GNU Lesser General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU General Public License
 17  * along with GlobWeb. If not, see <http://www.gnu.org/licenses/>.
 18  ***************************************/
 19 
 20  define (['./Utils', './Tile', './GeoBound', './CoordinateSystem'], function(Utils,Tile,GeoBound,CoordinateSystem) {
 21 
 22 /**************************************************************************************************************/
 23 
 24 /** @constructor
 25 	MercatorTiling constructor
 26  */
 27 var MercatorTiling = function(startLevel)
 28 {
 29 	this.startLevel = startLevel;
 30 }
 31 
 32 /**************************************************************************************************************/
 33 
 34 /** 
 35 	Generate the tiles for level zero
 36  */
 37 MercatorTiling.prototype.generateLevelZeroTiles = function(config)
 38 {
 39 	config.skirt = true;
 40 	config.cullSign = 1;
 41 	
 42 	var level0Tiles = [];
 43 	
 44 	var level0NumTilesX = Math.pow(2,this.startLevel);
 45 	var level0NumTilesY = Math.pow(2,this.startLevel);
 46 	
 47 	for (var j = 0; j < level0NumTilesY; j++)
 48 	{
 49 		for (var i = 0; i < level0NumTilesX; i++)
 50 		{
 51 			var tile = new MercatorTile( this.startLevel, i, j );
 52 			tile.config = config;
 53 			level0Tiles.push( tile );
 54 		}
 55 	}
 56 
 57 	return level0Tiles;
 58 }
 59 
 60 /**************************************************************************************************************/
 61 
 62 /** 
 63 	Locate a level zero tile
 64  */
 65 MercatorTiling.prototype.lonlat2LevelZeroIndex = function(lon,lat)
 66 {	
 67 	// TODO
 68 	return 0;
 69 }
 70 
 71 /**************************************************************************************************************/
 72 
 73 MercatorTiling.tile2long = function(x,z) {
 74 	return ( x /Math.pow(2,z) * 360 - 180 );
 75 }
 76 
 77 MercatorTiling.tile2lat = function(y,z) {
 78 	var n = Math.PI - 2 * Math.PI * y / Math.pow(2,z);
 79 	return ( 180 / Math.PI * Math.atan(0.5*(Math.exp(n)-Math.exp(-n))));
 80 }		
 81 
 82 /**************************************************************************************************************/
 83 
 84 /** @constructor
 85 	Tile constructor
 86  */
 87 var MercatorTile = function( level, x, y )
 88 {
 89     // Call ancestor constructor
 90     Tile.prototype.constructor.call(this);
 91 	
 92 	this.level = level;
 93 	this.x = x;
 94 	this.y = y;
 95 	
 96 	this.geoBound = new GeoBound( MercatorTiling.tile2long(x,level), MercatorTiling.tile2lat(y+1,level), MercatorTiling.tile2long(x+1,level), MercatorTiling.tile2lat(y,level) );
 97 }
 98 
 99 /**************************************************************************************************************/
100 
101 /** Inhertis from tile */
102 MercatorTile.prototype = new Tile;
103 
104 /**************************************************************************************************************/
105 
106 /** @export
107   Get elevation at a geo position
108 */
109 MercatorTile.prototype.getElevation = function(lon,lat)
110 {
111 	// TODO
112 	return 0.0;
113 }
114 
115 /**************************************************************************************************************/
116 
117 /**
118 	Create the children
119  */
120 MercatorTile.prototype.createChildren = function()
121 {
122 	// Create the children
123 	var tile00 = new MercatorTile( this.level+1, 2*this.x, 2*this.y );
124 	var tile10 = new MercatorTile( this.level+1, 2*this.x+1, 2*this.y );
125 	var tile01 = new MercatorTile( this.level+1, 2*this.x, 2*this.y+1 );
126 	var tile11 = new MercatorTile( this.level+1, 2*this.x+1, 2*this.y+1 );
127 	
128 	tile00.initFromParent( this, 0, 0 );
129 	tile10.initFromParent( this, 1, 0 );
130 	tile01.initFromParent( this, 0, 1 );
131 	tile11.initFromParent( this, 1, 1 );
132 	
133 	this.children = [ tile00, tile10, tile01, tile11 ];	
134 }
135 
136 /**************************************************************************************************************/
137 
138 /**
139 	Convert coordinates in longitude,latitude to coordinate in "tile space"
140 	Tile space means coordinates are between [0,tesselation-1] if inside the tile
141 	Used by renderers algorithm to clamp coordinates on the tile
142  */
143 MercatorTile.prototype.lonlat2tile = function(coordinates)
144 {
145 	var tpl = Math.pow(2,this.level);
146 	var factor = this.config.tesselation-1;
147 	
148 	var tileCoords = [];
149 	for ( var i = 0; i < coordinates.length; i++ )
150 	{
151 		var x = ( coordinates[i][0] + 180.) / 360.; 
152 		var sinLat = Math.sin(coordinates[i][1] * Math.PI / 180.);
153 		var y = 0.5 - Math.log((1 + sinLat) / (1 - sinLat)) / (4. * Math.PI);
154 		
155 		tileCoords.push( [ factor * (x * tpl - this.x), factor * (y * tpl - this.y) ] );	
156 	}
157 	
158 	return tileCoords;
159 }
160 
161 /**************************************************************************************************************/
162 
163 /**
164 	Generate vertices for tile
165  */
166 MercatorTile.prototype.generateVertices = function(elevations)
167 {	 
168 	// Compute tile matrix
169 	this.matrix = CoordinateSystem.getLHVTransform( this.geoBound.getCenter() );
170 	var invMatrix = mat4.create();
171 	mat4.inverse( this.matrix, invMatrix );
172 	this.inverseMatrix = invMatrix;
173 
174 	// Build the vertices
175 	var size = this.config.tesselation;
176 	var vertices = new Float32Array( 3*size*(size+6) );
177 	var step = 1.0 / (size-1);
178 	var radius = CoordinateSystem.radius;
179 	var scale = CoordinateSystem.heightScale;
180 	var offset = 0;
181 	
182 	var twoPowLevel = Math.pow(2,this.level);
183 	
184 	var v = this.y;
185 	for ( var j=0; j < size; j++)
186 	{
187 		var n = Math.PI * (1.0  - 2.0 * v / twoPowLevel);
188 		var lat = Math.atan( 0.5 * (Math.exp(n) - Math.exp(-n)) );
189 	
190 		var cosLat = Math.cos( lat );
191 		var sinLat = Math.sin( lat );
192 		
193 		var u = this.x;
194 				
195 		for ( var i=0; i < size; i++)
196 		{
197 			var lon = Math.PI * ( 2.0 * u / twoPowLevel - 1.0 );
198 			var height = elevations ? scale * elevations[ offset ] : 0.0;
199 			
200 			var x = (radius + height) * Math.cos( lon ) * cosLat;
201 			var y = (radius + height) * Math.sin( lon ) * cosLat;
202 			var z = (radius + height) * sinLat;
203 			
204 			var vertexOffset = offset * 3;
205 			vertices[vertexOffset] = invMatrix[0]*x + invMatrix[4]*y + invMatrix[8]*z + invMatrix[12];
206 			vertices[vertexOffset+1] = invMatrix[1]*x + invMatrix[5]*y + invMatrix[9]*z + invMatrix[13];
207 			vertices[vertexOffset+2] = invMatrix[2]*x + invMatrix[6]*y + invMatrix[10]*z + invMatrix[14];
208 						
209 			offset++;
210 			u += step;
211 		}
212 		
213 		v += step;
214 	}
215 	
216 	return vertices;
217 }
218 
219 
220 /**************************************************************************************************************/
221 
222 /**
223 	Override buildSkirtVertices for mercator.
224 	Use skirt to "fill" the pole
225  */
226  MercatorTile.prototype.buildSkirtVertices = function(center,srcOffset,srcStep,dstOffset)
227 {
228 	var size = this.config.tesselation;
229 	var vertexSize = this.config.vertexSize;
230 	var numTilesY = Math.pow(2,this.level);
231 	
232 	// Check if the tile is at the north (isTop) or south (isBottom) pole
233 	var isTop = this.y == 0 && dstOffset == vertexSize * (size * size);
234 	var isBottom = this.y == numTilesY-1 && dstOffset == vertexSize * ((size+1) * size);
235 		
236 	if ( isTop || isBottom )
237 	{
238 		var vertices = this.vertices;
239 		
240 		var pt = CoordinateSystem.fromGeoTo3D( isTop ? [ 0.0, 90.0, 0.0 ] : [ 0.0, -90.0, 0.0 ] );
241 		mat4.multiplyVec3( this.inverseMatrix, pt );
242 		
243 		for ( var i = 0; i < size; i++)
244 		{			
245 			vertices[ dstOffset ] = pt[0];
246 			vertices[ dstOffset+1 ] = pt[1];
247 			vertices[ dstOffset+2 ] = pt[2];
248 			
249 			for (var n = 3; n < vertexSize; n++)
250 			{
251 				vertices[ dstOffset+n ] = vertices[srcOffset+n];
252 			}
253 			
254 			dstOffset += vertexSize;
255 		}	
256 		
257 		// Recompute the bbox to have correct culling
258 		//this.bbox.compute(this.vertices,dstOffset + vertexSize*size,vertexSize);
259 		//this.radius = this.bbox.getRadius();
260 	}
261 	else
262 	{
263 		Tile.prototype.buildSkirtVertices.call(this,center,srcOffset,srcStep,dstOffset);
264 	}
265 }
266 
267 /**************************************************************************************************************/
268 
269 return MercatorTiling;
270 
271 });
272