]> git.openstreetmap.org Git - rails.git/blob - public/lib/OpenLayers/Layer/Grid.js
Test and fix for issue #1568. Wasn't testing for end element in the right place.
[rails.git] / public / lib / OpenLayers / Layer / Grid.js
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(), {
10     
11     // str: url
12     url: null,
13
14     // hash: params
15     params: null,
16
17     // tileSize: OpenLayers.Size
18     tileSize: null,
19     
20     // grid: Array(Array())
21     // this is an array of rows, each row is an array of tiles
22     grid: null,
23
24     /**
25     * @param {str} name
26     * @param {str} url
27     * @param {hash} params
28     */
29     initialize: function(name, url, params) {
30         var newArguments = arguments;
31         if (arguments.length > 0) {
32             newArguments = [name];
33         }          
34         OpenLayers.Layer.prototype.initialize.apply(this, newArguments);
35         this.url = url;
36         this.params = params;
37         this.tileSize = new OpenLayers.Size(OpenLayers.Layer.Grid.TILE_WIDTH,
38                                             OpenLayers.Layer.Grid.TILE_HEIGHT);
39     },
40
41     /**
42      * 
43      */
44     destroy: function() {
45         this.params = null;
46         this.clearGrid();
47         this.grid = null;
48         OpenLayers.Layer.prototype.destroy.apply(this, arguments); 
49     },
50
51     setTileSize: function (size) {
52         this.tileSize = size.copyOf();
53     },
54
55     /** 
56      * moveTo
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}
61      */
62     moveTo:function(bounds,zoomChanged) {
63         if (!this.getVisibility()) {
64             if (zoomChanged) {
65                 this.grid = null;
66             }
67             return;
68         }
69         if (!this.grid || zoomChanged) {
70             this._initTiles();
71         } else { 
72             var i = 0;
73             while (this.getGridBounds().bottom > bounds.bottom) {
74                this.insertRow(false); 
75             }
76             while (this.getGridBounds().left > bounds.left) {
77                this.insertColumn(true); 
78             }
79             while (this.getGridBounds().top < bounds.top) {
80                this.insertRow(true); 
81             }
82             while (this.getGridBounds().right < bounds.right) {
83                this.insertColumn(false); 
84             }
85         }
86     },
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);
94     },
95     
96     /**
97     */
98     _initTiles:function() {
99
100         //first of all, clear out the main div
101         this.div.innerHTML = "";
102
103         //now clear out the old grid and start a new one
104         this.clearGrid();
105         this.grid = new Array();
106
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;
113         
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;
119         
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;
125         
126         tileoffsetx = Math.round(tileoffsetx); // heaven help us
127         tileoffsety = Math.round(tileoffsety);
128
129         this.origin = new OpenLayers.Pixel(tileoffsetx,tileoffsety);
130
131         var startX = tileoffsetx; 
132         var startLon = tileoffsetlon;
133         
134         do {
135             var row = new Array();
136             this.grid.append(row);
137             tileoffsetlon = startLon;
138             tileoffsetx = startX;
139             do {
140                 var tileBounds = new OpenLayers.Bounds(tileoffsetlon, 
141                                                        tileoffsetlat, 
142                                                        tileoffsetlon+tilelon,
143                                                        tileoffsetlat+tilelat);
144
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))
148                                                             );
149                 tile.draw((this.params.TRANSPARENT == 'true'));
150                 row.append(tile);
151      
152                 tileoffsetlon += tilelon;       
153                 tileoffsetx += this.tileSize.w;
154             } while (tileoffsetlon < bounds.right)  
155             
156             tileoffsetlat -= tilelat;
157             tileoffsety += this.tileSize.h;
158         } while(tileoffsetlat > bounds.bottom - tilelat)
159
160     },
161     
162     /**
163     * @param {bool} prepend - if true, prepend to beginning.
164     *                         if false, then append to end
165     */
166     insertRow:function(prepend) {
167         var modelRowIndex = (prepend) ? 0 : (this.grid.length - 1);
168         var modelRow = this.grid[modelRowIndex];
169
170         var newRow = new Array();
171
172         var resolution = this.map.getResolution();
173         var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h;
174         var deltaLat = resolution * -deltaY;
175
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);
186         }
187         
188         if (newRow.length>0){
189             if (prepend) {
190                 this.grid.prepend(newRow);
191             } else {
192                 this.grid.append(newRow);
193             }
194         }       
195     },
196
197     /**
198     * @param {bool} prepend - if true, prepend to beginning.
199     *                         if false, then append to end
200     */
201     insertColumn:function(prepend) {
202         var modelCellIndex;
203         var deltaX = (prepend) ? -this.tileSize.w : this.tileSize.w;
204         var resolution = this.map.getResolution();
205         var deltaLon = resolution * deltaX;
206
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];
211             
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'));
219             
220             if (prepend) {
221                 row = row.prepend(newTile);
222             } else {
223                 row = row.append(newTile);
224             }
225         }
226     },
227     /** combine the ds's serverPath with its params and the tile's params. 
228     *   
229     *    does checking on the serverPath variable, allowing for cases when it 
230     *     is supplied with trailing ? or &, as well as cases where not. 
231     *
232     *    return in formatted string like this:
233     *        "server?key1=value1&key2=value2&key3=value3"
234     *
235     * @return {str}
236     */
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);
243
244         var server = this.url;
245         var lastServerChar = server.charAt(server.length - 1);
246
247         if ((lastServerChar == "&") || (lastServerChar == "?")) {
248             requestString = server + paramsString;
249         } else {
250             if (server.indexOf('?') == -1) {
251                 //serverPath has no ? -- add one
252                 requestString = server + '?' + paramsString;
253             } else {
254                 //serverPath contains ?, so must already have paramsString at the end
255                 requestString = server + '&' + paramsString;
256             }
257         }
258         return requestString;
259     },
260     
261     /** go through and remove all tiles from the grid, calling
262     *    destroy() on each of them to kill circular references
263     * 
264     * @private
265     */
266     clearGrid:function() {
267         if (this.grid) {
268             while(this.grid.length > 0) {
269                 var row = this.grid[0];
270                 while(row.length > 0) {
271                     var tile = row[0];
272                     tile.destroy();
273                     row.remove(tile);
274                 }
275                 this.grid.remove(row);                   
276             }
277         }
278     },
279
280     /**
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.
284     *
285     * @param {OpenLayers.Bounds} bounds
286     *
287     * @returns The added OpenLayers.Tile
288     * @type OpenLayers.Tile
289     */
290     addTile:function(bounds,position) {
291         // Should be implemented by subclasses
292     },
293
294     /** @final @type String */
295     CLASS_NAME: "OpenLayers.Grid"
296 });