1 /* Copyright (c) 2006 MetaCarta, Inc., published under the BSD license.
 
   2  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the full
 
   3  * text of the license. */
 
   4 // @require: OpenLayers/Layer.js
 
   5 // @require: OpenLayers/Util.js
 
   6 OpenLayers.Layer.Grid = Class.create();
 
   7 OpenLayers.Layer.Grid.TILE_WIDTH = 256;
 
   8 OpenLayers.Layer.Grid.TILE_HEIGHT = 256;
 
   9 OpenLayers.Layer.Grid.prototype = Object.extend( new OpenLayers.Layer(), {
 
  17     // tileSize: OpenLayers.Size
 
  20     // grid: Array(Array())
 
  21     // this is an array of rows, each row is an array of tiles
 
  27     * @param {hash} params
 
  29     initialize: function(name, url, params) {
 
  30         var newArguments = arguments;
 
  31         if (arguments.length > 0) {
 
  32             newArguments = [name];
 
  34         OpenLayers.Layer.prototype.initialize.apply(this, newArguments);
 
  37         this.tileSize = new OpenLayers.Size(OpenLayers.Layer.Grid.TILE_WIDTH,
 
  38                                             OpenLayers.Layer.Grid.TILE_HEIGHT);
 
  48         OpenLayers.Layer.prototype.destroy.apply(this, arguments); 
 
  51     setTileSize: function (size) {
 
  52         this.tileSize = size.copyOf();
 
  57      * moveTo is a function called whenever the map is moved. All the moving
 
  58      * of actual 'tiles' is done by the map, but moveTo's role is to accept
 
  59      * a bounds and make sure the data that that bounds requires is pre-loaded.
 
  60      * @param {OpenLayers.Bounds}
 
  62     moveTo:function(bounds,zoomChanged) {
 
  63         if (!this.getVisibility()) {
 
  69         if (!this.grid || zoomChanged) {
 
  73             while (this.getGridBounds().bottom > bounds.bottom) {
 
  74                this.insertRow(false); 
 
  76             while (this.getGridBounds().left > bounds.left) {
 
  77                this.insertColumn(true); 
 
  79             while (this.getGridBounds().top < bounds.top) {
 
  82             while (this.getGridBounds().right < bounds.right) {
 
  83                this.insertColumn(false); 
 
  87     getGridBounds:function() {
 
  88         var topLeftTile = this.grid[0][0];
 
  89         var bottomRightTile = this.grid[this.grid.length-1][this.grid[0].length-1];
 
  90         return new OpenLayers.Bounds(topLeftTile.bounds.left, 
 
  91                                      bottomRightTile.bounds.bottom,
 
  92                                      bottomRightTile.bounds.right, 
 
  93                                      topLeftTile.bounds.top);
 
  98     _initTiles:function() {
 
 100         //first of all, clear out the main div
 
 101         this.div.innerHTML = "";
 
 103         //now clear out the old grid and start a new one
 
 105         this.grid = new Array();
 
 107         var viewSize = this.map.getSize();
 
 108         var bounds = this.map.getExtent();
 
 109         var extent = this.map.getFullExtent();
 
 110         var resolution = this.map.getResolution();
 
 111         var tilelon = resolution*this.tileSize.w;
 
 112         var tilelat = resolution*this.tileSize.h;
 
 114         var offsetlon = bounds.left - extent.left;
 
 115         var tilecol = Math.floor(offsetlon/tilelon);
 
 116         var tilecolremain = offsetlon/tilelon - tilecol;
 
 117         var tileoffsetx = -tilecolremain * this.tileSize.w;
 
 118         var tileoffsetlon = extent.left + tilecol * tilelon;
 
 120         var offsetlat = bounds.top - (extent.bottom + tilelat);  
 
 121         var tilerow = Math.ceil(offsetlat/tilelat);
 
 122         var tilerowremain = tilerow - offsetlat/tilelat;
 
 123         var tileoffsety = -tilerowremain * this.tileSize.h;
 
 124         var tileoffsetlat = extent.bottom + tilerow * tilelat;
 
 126         tileoffsetx = Math.round(tileoffsetx); // heaven help us
 
 127         tileoffsety = Math.round(tileoffsety);
 
 129         this.origin = new OpenLayers.Pixel(tileoffsetx,tileoffsety);
 
 131         var startX = tileoffsetx; 
 
 132         var startLon = tileoffsetlon;
 
 135             var row = new Array();
 
 136             this.grid.append(row);
 
 137             tileoffsetlon = startLon;
 
 138             tileoffsetx = startX;
 
 140                 var tileBounds = new OpenLayers.Bounds(tileoffsetlon, 
 
 142                                                        tileoffsetlon+tilelon,
 
 143                                                        tileoffsetlat+tilelat);
 
 145                 var tile = this.addTile(tileBounds, 
 
 146                                         new OpenLayers.Pixel(tileoffsetx - parseInt(this.map.layerContainerDiv.style.left),
 
 147                                                              tileoffsety - parseInt(this.map.layerContainerDiv.style.top))
 
 149                 tile.draw((this.params.TRANSPARENT == 'true'));
 
 152                 tileoffsetlon += tilelon;       
 
 153                 tileoffsetx += this.tileSize.w;
 
 154             } while (tileoffsetlon < bounds.right)  
 
 156             tileoffsetlat -= tilelat;
 
 157             tileoffsety += this.tileSize.h;
 
 158         } while(tileoffsetlat > bounds.bottom - tilelat)
 
 163     * @param {bool} prepend - if true, prepend to beginning.
 
 164     *                         if false, then append to end
 
 166     insertRow:function(prepend) {
 
 167         var modelRowIndex = (prepend) ? 0 : (this.grid.length - 1);
 
 168         var modelRow = this.grid[modelRowIndex];
 
 170         var newRow = new Array();
 
 172         var resolution = this.map.getResolution();
 
 173         var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h;
 
 174         var deltaLat = resolution * -deltaY;
 
 176         for (var i=0; i < modelRow.length; i++) {
 
 177             var modelTile = modelRow[i];
 
 178             var bounds = modelTile.bounds.copyOf();
 
 179             var position = modelTile.position.copyOf();
 
 180             bounds.bottom = bounds.bottom + deltaLat;
 
 181             bounds.top = bounds.top + deltaLat;
 
 182             position.y = position.y + deltaY;
 
 183             var newTile = this.addTile(bounds, position);
 
 184             newTile.draw((this.params.TRANSPARENT == 'true'));
 
 185             newRow.append(newTile);
 
 188         if (newRow.length>0){
 
 190                 this.grid.prepend(newRow);
 
 192                 this.grid.append(newRow);
 
 198     * @param {bool} prepend - if true, prepend to beginning.
 
 199     *                         if false, then append to end
 
 201     insertColumn:function(prepend) {
 
 203         var deltaX = (prepend) ? -this.tileSize.w : this.tileSize.w;
 
 204         var resolution = this.map.getResolution();
 
 205         var deltaLon = resolution * deltaX;
 
 207         for (var i=0; i<this.grid.length; i++) {
 
 208             var row = this.grid[i];
 
 209             modelTileIndex = (prepend) ? 0 : (row.length - 1);
 
 210             var modelTile = row[modelTileIndex];
 
 212             var bounds = modelTile.bounds.copyOf();
 
 213             var position = modelTile.position.copyOf();
 
 214             bounds.left = bounds.left + deltaLon;
 
 215             bounds.right = bounds.right + deltaLon;
 
 216             position.x = position.x + deltaX;
 
 217             var newTile = this.addTile(bounds, position);
 
 218             newTile.draw((this.params.TRANSPARENT == 'true'));
 
 221                 row = row.prepend(newTile);
 
 223                 row = row.append(newTile);
 
 227     /** combine the ds's serverPath with its params and the tile's params. 
 
 229     *    does checking on the serverPath variable, allowing for cases when it 
 
 230     *     is supplied with trailing ? or &, as well as cases where not. 
 
 232     *    return in formatted string like this:
 
 233     *        "server?key1=value1&key2=value2&key3=value3"
 
 237     getFullRequestString:function(params) {
 
 238         var requestString = "";        
 
 239         this.params.SRS = this.map.projection;
 
 240         // concat tile params with layer params and convert to string
 
 241         var allParams = Object.extend(this.params, params);
 
 242         var paramsString = OpenLayers.Util.getParameterString(allParams);
 
 244         var server = this.url;
 
 245         var lastServerChar = server.charAt(server.length - 1);
 
 247         if ((lastServerChar == "&") || (lastServerChar == "?")) {
 
 248             requestString = server + paramsString;
 
 250             if (server.indexOf('?') == -1) {
 
 251                 //serverPath has no ? -- add one
 
 252                 requestString = server + '?' + paramsString;
 
 254                 //serverPath contains ?, so must already have paramsString at the end
 
 255                 requestString = server + '&' + paramsString;
 
 258         return requestString;
 
 261     /** go through and remove all tiles from the grid, calling
 
 262     *    destroy() on each of them to kill circular references
 
 266     clearGrid:function() {
 
 268             while(this.grid.length > 0) {
 
 269                 var row = this.grid[0];
 
 270                 while(row.length > 0) {
 
 275                 this.grid.remove(row);                   
 
 281     * addTile gives subclasses of Grid the opportunity to create an 
 
 282     * OpenLayer.Tile of their choosing. The implementer should initialize 
 
 283     * the new tile and take whatever steps necessary to display it.
 
 285     * @param {OpenLayers.Bounds} bounds
 
 287     * @returns The added OpenLayers.Tile
 
 288     * @type OpenLayers.Tile
 
 290     addTile:function(bounds,position) {
 
 291         // Should be implemented by subclasses
 
 294     /** @final @type String */
 
 295     CLASS_NAME: "OpenLayers.Grid"