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 	GeoTiling constructor
 26  */
 27 var GeoTiling = function(nx,ny)
 28 {
 29 	this.level0NumTilesX = nx;
 30 	this.level0NumTilesY = ny;
 31 }
 32 
 33 /**************************************************************************************************************/
 34 
 35 /** 
 36 	Generate the tiles for level zero
 37  */
 38 GeoTiling.prototype.generateLevelZeroTiles = function(config)
 39 {	
 40 	config.skirt = 1;
 41 	config.cullSign = 1;
 42 
 43 	var level0Tiles = [];
 44 	
 45 	var latStep = 180 / this.level0NumTilesY;
 46 	var lonStep = 360 / this.level0NumTilesX;
 47 	
 48 	for (var j = 0; j < this.level0NumTilesY; j++)
 49 	{
 50 		for (var i = 0; i < this.level0NumTilesX; i++)
 51 		{
 52 			var geoBound = new GeoBound( -180 + i * lonStep, 90 - (j+1) * latStep, -180 + (i+1) * lonStep, 90 - j * latStep  );
 53 			var tile = new GeoTile(geoBound, 0, i, j)
 54 			tile.config = config;
 55 			level0Tiles.push( tile );
 56 		}
 57 	}
 58 
 59 	return level0Tiles;
 60 }
 61 
 62 /**************************************************************************************************************/
 63 
 64 /** 
 65 	Locate a level zero tile
 66  */
 67 GeoTiling.prototype.lonlat2LevelZeroIndex = function(lon,lat)
 68 {	
 69 	var i = Math.floor( (lon + 180) * this.level0NumTilesX / 360 );
 70  	var j = Math.floor( (90 - lat) * this.level0NumTilesY / 180 );
 71 	return j * this.level0NumTilesX + i;
 72 
 73 }
 74 
 75 /**************************************************************************************************************/
 76 
 77 /** @constructor
 78 	Tile constructor
 79  */
 80 var GeoTile = function( geoBound, level, x, y )
 81 {
 82     // Call ancestor constructor
 83     Tile.prototype.constructor.call(this);
 84 	
 85 	this.geoBound = geoBound;
 86 	this.level = level;
 87 	this.x = x;
 88 	this.y = y;
 89 }
 90 
 91 /**************************************************************************************************************/
 92 
 93 /** inherits from Tile */
 94 GeoTile.prototype = new Tile;
 95 
 96 /**************************************************************************************************************/
 97 
 98 /** @export
 99   Get elevation at a geo position
100 */
101 GeoTile.prototype.getElevation = function(lon,lat)
102 {
103 	// Get the lon/lat in coordinates between [0,1] in the tile
104 	var u = (lon - this.geoBound.west) / (this.geoBound.east - this.geoBound.west);
105  	var v = (lat - this.geoBound.north) / (this.geoBound.south - this.geoBound.north);
106 
107 	// Quick fix when lat is on the border of the tile
108 	var childIndex = (v >= 1 ? 1 : Math.floor(2*v) )*2 + Math.floor(2*u);
109 	if ( this.children && this.children[childIndex].state == Tile.State.LOADED )
110 		return this.children[childIndex].getElevation(lon,lat);
111 	
112 	var tess = this.config.tesselation;
113 	var i = Math.floor( u * tess );
114 	var j = Math.floor( v * tess );
115 	
116 	var vo = this.config.vertexSize * (j * tess + i);
117 	var vertex = [ this.vertices[vo], this.vertices[vo+1], this.vertices[vo+2] ];
118 	mat4.multiplyVec3( this.matrix, vertex );
119 	var geo = CoordinateSystem.from3DToGeo(vertex);
120 	return geo[2];
121 }
122 
123 /**************************************************************************************************************/
124 
125 /**
126 	Create the children
127  */
128 GeoTile.prototype.createChildren = function()
129 {
130 	// Create the children
131 	var lonCenter = ( this.geoBound.east + this.geoBound.west ) * 0.5;
132 	var latCenter = ( this.geoBound.north + this.geoBound.south ) * 0.5;
133 	
134 	var level = this.level+1;
135 	
136 	var tile00 = new GeoTile( new GeoBound( this.geoBound.west, latCenter, lonCenter, this.geoBound.north), level, 2*this.x, 2*this.y );
137 	var tile10 = new GeoTile( new GeoBound( lonCenter, latCenter,  this.geoBound.east, this.geoBound.north), level, 2*this.x+1, 2*this.y );
138 	var tile01 = new GeoTile( new GeoBound( this.geoBound.west, this.geoBound.south, lonCenter, latCenter), level, 2*this.x, 2*this.y+1 );
139 	var tile11 = new GeoTile( new GeoBound( lonCenter, this.geoBound.south, this.geoBound.east, latCenter), level, 2*this.x+1, 2*this.y+1  );
140 	
141 	tile00.initFromParent( this, 0, 0 );
142 	tile10.initFromParent( this, 1, 0 );
143 	tile01.initFromParent( this, 0, 1 );
144 	tile11.initFromParent( this, 1, 1 );
145 	
146 	this.children = [ tile00, tile10, tile01, tile11 ];	
147 }
148 
149 /**************************************************************************************************************/
150 
151 /**
152 	Convert coordinates in longitude,latitude to coordinate in "tile space"
153 	Tile space means coordinates are between [0,tesselation-1] if inside the tile
154 	Used by renderers algorithm to clamp coordinates on the tile
155  */
156 GeoTile.prototype.lonlat2tile = function(coordinates)
157 {
158 	var ul = this.geoBound.east - this.geoBound.west;
159 	var vl = this.geoBound.south - this.geoBound.north;
160 	var factor = this.config.tesselation-1;
161 	
162 	var tileCoords = [];
163 	for ( var i = 0; i < coordinates.length; i++ )
164 	{
165 		var u = factor * (coordinates[i][0] - this.geoBound.west) / ul;
166 		var v = factor * (coordinates[i][1] - this.geoBound.north) / vl;
167 		tileCoords.push( [ u, v ] );	
168 	}
169 	
170 	return tileCoords;
171 }
172 
173 /**************************************************************************************************************/
174 
175 /**
176 	Generate vertices for tile
177  */
178 GeoTile.prototype.generateVertices = function(elevations)
179 {	
180 	// Compute tile matrix
181 	this.matrix = CoordinateSystem.getLHVTransform( this.geoBound.getCenter() );
182 	var invMatrix = mat4.create();
183 	mat4.inverse( this.matrix, invMatrix );
184 	this.inverseMatrix = invMatrix;
185 	
186 	// Build the vertices
187 	var vertexSize = this.config.vertexSize;
188 	var size = this.config.tesselation;
189 	var vertices = new Float32Array( vertexSize*size*(size+6) );
190 	var lonStep = (this.geoBound.east - this.geoBound.west) / (size-1);
191 	var latStep = (this.geoBound.south - this.geoBound.north) / (size-1);
192 	var radius = CoordinateSystem.radius;
193 	var scale = CoordinateSystem.heightScale;
194 	var offset = 0;
195 	
196 	var lat = this.geoBound.north * Math.PI / 180.0;
197 	latStep = latStep * Math.PI / 180.0;
198 	lonStep = lonStep * Math.PI / 180.0;
199 	for ( var j=0; j < size; j++)
200 	{
201 		var cosLat = Math.cos( lat );
202 		var sinLat = Math.sin( lat );
203 		
204 		var lon = this.geoBound.west * Math.PI / 180.0;
205 				
206 		for ( var i=0; i < size; i++)
207 		{
208 			var height = elevations ? scale * elevations[ offset ] : 0.0;
209 			
210 			var x = (radius + height) * Math.cos( lon ) * cosLat;
211 			var y = (radius + height) * Math.sin( lon ) * cosLat;
212 			var z = (radius + height) * sinLat;
213 			
214 			var vi = offset * vertexSize;
215 			vertices[vi] = invMatrix[0]*x + invMatrix[4]*y + invMatrix[8]*z + invMatrix[12];
216 			vertices[vi+1] = invMatrix[1]*x + invMatrix[5]*y + invMatrix[9]*z + invMatrix[13];
217 			vertices[vi+2] = invMatrix[2]*x + invMatrix[6]*y + invMatrix[10]*z + invMatrix[14];
218 						
219 			offset++;
220 			lon += lonStep;
221 		}
222 		
223 		lat += latStep;
224 	}
225 	
226 	return vertices;
227 }
228 
229 /**************************************************************************************************************/
230 
231 return GeoTiling;
232 
233 });
234