52bd252d34f7c08a71754c17f0a0c7be94cbbcd0.svn-base
22.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
* full list of contributors). Published under the Clear BSD license.
* See http://svn.openlayers.org/trunk/openlayers/license.txt for the
* full text of the license. */
/**
* @requires OpenLayers/Tile.js
*/
/**
* Class: OpenLayers.Tile.Image
* Instances of OpenLayers.Tile.Image are used to manage the image tiles
* used by various layers. Create a new image tile with the
* <OpenLayers.Tile.Image> constructor.
*
* Inherits from:
* - <OpenLayers.Tile>
*/
OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
/**
* Property: url
* {String} The URL of the image being requested. No default. Filled in by
* layer.getURL() function.
*/
url: null,
/**
* Property: imgDiv
* {DOMElement} The div element which wraps the image.
*/
imgDiv: null,
/**
* Property: frame
* {DOMElement} The image element is appended to the frame. Any gutter on
* the image will be hidden behind the frame.
*/
frame: null,
/**
* Property: layerAlphaHack
* {Boolean} True if the png alpha hack needs to be applied on the layer's div.
*/
layerAlphaHack: null,
/**
* Property: isBackBuffer
* {Boolean} Is this tile a back buffer tile?
*/
isBackBuffer: false,
/**
* Property: isFirstDraw
* {Boolean} Is this the first time the tile is being drawn?
* This is used to force resetBackBuffer to synchronize
* the backBufferTile with the foreground tile the first time
* the foreground tile loads so that if the user zooms
* before the layer has fully loaded, the backBufferTile for
* tiles that have been loaded can be used.
*/
isFirstDraw: true,
/**
* Property: backBufferTile
* {<OpenLayers.Tile>} A clone of the tile used to create transition
* effects when the tile is moved or changes resolution.
*/
backBufferTile: null,
/**
* APIProperty: maxGetUrlLength
* {Number} If set, requests that would result in GET urls with more
* characters than the number provided will be made using form-encoded
* HTTP POST. It is good practice to avoid urls that are longer than 2048
* characters.
*
* Caution:
* Older versions of Gecko based browsers (e.g. Firefox < 3.5) and
* Opera < 10.0 do not fully support this option.
*
* Note:
* Do not use this option for layers that have a transitionEffect
* configured - IFrame tiles from POST requests can not be resized.
*/
maxGetUrlLength: null,
/** TBD 3.0 - reorder the parameters to the init function to remove
* URL. the getUrl() function on the layer gets called on
* each draw(), so no need to specify it here.
*
* Constructor: OpenLayers.Tile.Image
* Constructor for a new <OpenLayers.Tile.Image> instance.
*
* Parameters:
* layer - {<OpenLayers.Layer>} layer that the tile will go in.
* position - {<OpenLayers.Pixel>}
* bounds - {<OpenLayers.Bounds>}
* url - {<String>} Deprecated. Remove me in 3.0.
* size - {<OpenLayers.Size>}
* options - {Object}
*/
initialize: function(layer, position, bounds, url, size, options) {
OpenLayers.Tile.prototype.initialize.apply(this, arguments);
if (this.maxGetUrlLength != null) {
OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);
}
this.url = url; //deprecated remove me
this.frame = document.createElement('div');
this.frame.style.overflow = 'hidden';
this.frame.style.position = 'absolute';
this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();
},
/**
* APIMethod: destroy
* nullify references to prevent circular references and memory leaks
*/
destroy: function() {
if (this.imgDiv != null) {
this.removeImgDiv();
}
this.imgDiv = null;
if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) {
this.layer.div.removeChild(this.frame);
}
this.frame = null;
/* clean up the backBufferTile if it exists */
if (this.backBufferTile) {
this.backBufferTile.destroy();
this.backBufferTile = null;
}
this.layer.events.unregister("loadend", this, this.resetBackBuffer);
OpenLayers.Tile.prototype.destroy.apply(this, arguments);
},
/**
* Method: clone
*
* Parameters:
* obj - {<OpenLayers.Tile.Image>} The tile to be cloned
*
* Returns:
* {<OpenLayers.Tile.Image>} An exact clone of this <OpenLayers.Tile.Image>
*/
clone: function (obj) {
if (obj == null) {
obj = new OpenLayers.Tile.Image(this.layer,
this.position,
this.bounds,
this.url,
this.size);
}
//pick up properties from superclass
obj = OpenLayers.Tile.prototype.clone.apply(this, [obj]);
//dont want to directly copy the image div
obj.imgDiv = null;
return obj;
},
/**
* Method: draw
* Check that a tile should be drawn, and draw it.
*
* Returns:
* {Boolean} Always returns true.
*/
draw: function() {
if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
this.bounds = this.getBoundsFromBaseLayer(this.position);
}
var drawTile = OpenLayers.Tile.prototype.draw.apply(this, arguments);
if ((OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) ||
this.layer.singleTile) {
if (drawTile) {
//we use a clone of this tile to create a double buffer for visual
//continuity. The backBufferTile is used to create transition
//effects while the tile in the grid is repositioned and redrawn
if (!this.backBufferTile) {
this.backBufferTile = this.clone();
this.backBufferTile.hide();
// this is important. It allows the backBuffer to place itself
// appropriately in the DOM. The Image subclass needs to put
// the backBufferTile behind the main tile so the tiles can
// load over top and display as soon as they are loaded.
this.backBufferTile.isBackBuffer = true;
// potentially end any transition effects when the tile loads
this.events.register('loadend', this, this.resetBackBuffer);
// clear transition back buffer tile only after all tiles in
// this layer have loaded to avoid visual glitches
this.layer.events.register("loadend", this, this.resetBackBuffer);
}
// run any transition effects
this.startTransition();
} else {
// if we aren't going to draw the tile, then the backBuffer should
// be hidden too!
if (this.backBufferTile) {
this.backBufferTile.clear();
}
}
} else {
if (drawTile && this.isFirstDraw) {
this.events.register('loadend', this, this.showTile);
this.isFirstDraw = false;
}
}
if (!drawTile) {
return false;
}
if (this.isLoading) {
//if we're already loading, send 'reload' instead of 'loadstart'.
this.events.triggerEvent("reload");
} else {
this.isLoading = true;
this.events.triggerEvent("loadstart");
}
return this.renderTile();
},
/**
* Method: resetBackBuffer
* Triggered by two different events, layer loadend, and tile loadend.
* In any of these cases, we check to see if we can hide the
* backBufferTile yet and update its parameters to match the
* foreground tile.
*
* Basic logic:
* - If the backBufferTile hasn't been drawn yet, reset it
* - If layer is still loading, show foreground tile but don't hide
* the backBufferTile yet
* - If layer is done loading, reset backBuffer tile and show
* foreground tile
*/
resetBackBuffer: function() {
this.showTile();
if (this.backBufferTile &&
(this.isFirstDraw || !this.layer.numLoadingTiles)) {
this.isFirstDraw = false;
// check to see if the backBufferTile is within the max extents
// before rendering it
var maxExtent = this.layer.maxExtent;
var withinMaxExtent = (maxExtent &&
this.bounds.intersectsBounds(maxExtent, false));
if (withinMaxExtent) {
this.backBufferTile.position = this.position;
this.backBufferTile.bounds = this.bounds;
this.backBufferTile.size = this.size;
this.backBufferTile.imageSize = this.layer.getImageSize(this.bounds) || this.size;
this.backBufferTile.imageOffset = this.layer.imageOffset;
this.backBufferTile.resolution = this.layer.getResolution();
this.backBufferTile.renderTile();
}
this.backBufferTile.hide();
}
},
/**
* Method: renderTile
* Internal function to actually initialize the image tile,
* position it correctly, and set its url.
*/
renderTile: function() {
if (this.layer.async) {
this.initImgDiv();
// Asyncronous image requests call the asynchronous getURL method
// on the layer to fetch an image that covers 'this.bounds', in the scope of
// 'this', setting the 'url' property of the layer itself, and running
// the callback 'positionFrame' when the image request returns.
this.layer.getURLasync(this.bounds, this, "url", this.positionImage);
} else {
// syncronous image requests get the url and position the frame immediately,
// and don't wait for an image request to come back.
this.url = this.layer.getURL(this.bounds);
this.initImgDiv();
// position the frame immediately
this.positionImage();
}
return true;
},
/**
* Method: positionImage
* Using the properties currenty set on the layer, position the tile correctly.
* This method is used both by the async and non-async versions of the Tile.Image
* code.
*/
positionImage: function() {
// if the this layer doesn't exist at the point the image is
// returned, do not attempt to use it for size computation
if (this.layer === null) {
return;
}
// position the frame
OpenLayers.Util.modifyDOMElement(this.frame,
null, this.position, this.size);
var imageSize = this.layer.getImageSize(this.bounds);
if (this.layerAlphaHack) {
OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv,
null, null, imageSize, this.url);
} else {
OpenLayers.Util.modifyDOMElement(this.imgDiv,
null, null, imageSize) ;
this.imgDiv.src = this.url;
}
},
/**
* Method: clear
* Clear the tile of any bounds/position-related data so that it can
* be reused in a new location.
*/
clear: function() {
if(this.imgDiv) {
this.hide();
if (OpenLayers.Tile.Image.useBlankTile) {
this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif";
}
}
},
/**
* Method: initImgDiv
* Creates the imgDiv property on the tile.
*/
initImgDiv: function() {
if (this.imgDiv == null) {
var offset = this.layer.imageOffset;
var size = this.layer.getImageSize(this.bounds);
if (this.layerAlphaHack) {
this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null,
offset,
size,
null,
"relative",
null,
null,
null,
true);
} else {
this.imgDiv = OpenLayers.Util.createImage(null,
offset,
size,
null,
"relative",
null,
null,
true);
}
// needed for changing to a different server for onload error
if (OpenLayers.Util.isArray(this.layer.url)) {
this.imgDiv.urls = this.layer.url.slice();
}
this.imgDiv.className = 'olTileImage';
/* checkImgURL used to be used to called as a work around, but it
ended up hiding problems instead of solving them and broke things
like relative URLs. See discussion on the dev list:
http://openlayers.org/pipermail/dev/2007-January/000205.html
OpenLayers.Event.observe( this.imgDiv, "load",
OpenLayers.Function.bind(this.checkImgURL, this) );
*/
this.frame.style.zIndex = this.isBackBuffer ? 0 : 1;
this.frame.appendChild(this.imgDiv);
this.layer.div.appendChild(this.frame);
if(this.layer.opacity != null) {
OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null, null,
null, null, null,
this.layer.opacity);
}
// we need this reference to check back the viewRequestID
this.imgDiv.map = this.layer.map;
//bind a listener to the onload of the image div so that we
// can register when a tile has finished loading.
var onload = function() {
//normally isLoading should always be true here but there are some
// right funky conditions where loading and then reloading a tile
// with the same url *really*fast*. this check prevents sending
// a 'loadend' if the msg has already been sent
//
if (this.isLoading) {
this.isLoading = false;
this.events.triggerEvent("loadend");
}
};
if (this.layerAlphaHack) {
OpenLayers.Event.observe(this.imgDiv.childNodes[0], 'load',
OpenLayers.Function.bind(onload, this));
} else {
OpenLayers.Event.observe(this.imgDiv, 'load',
OpenLayers.Function.bind(onload, this));
}
// Bind a listener to the onerror of the image div so that we
// can registere when a tile has finished loading with errors.
var onerror = function() {
// If we have gone through all image reload attempts, it is time
// to realize that we are done with this image. Since
// OpenLayers.Util.onImageLoadError already has taken care about
// the error, we can continue as if the image was loaded
// successfully.
if (this.imgDiv._attempts > OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
onload.call(this);
}
};
OpenLayers.Event.observe(this.imgDiv, "error",
OpenLayers.Function.bind(onerror, this));
}
this.imgDiv.viewRequestID = this.layer.map.viewRequestID;
},
/**
* Method: removeImgDiv
* Removes the imgDiv from the DOM and stops listening to events on it.
*/
removeImgDiv: function() {
// unregister the "load" and "error" handlers. Only the "error" handler if
// this.layerAlphaHack is true.
OpenLayers.Event.stopObservingElement(this.imgDiv);
if (this.imgDiv.parentNode == this.frame) {
this.frame.removeChild(this.imgDiv);
this.imgDiv.map = null;
}
this.imgDiv.urls = null;
var child = this.imgDiv.firstChild;
//check for children (alphaHack img or IFrame)
if (child) {
OpenLayers.Event.stopObservingElement(child);
this.imgDiv.removeChild(child);
delete child;
} else {
// abort any currently loading image
this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif";
}
},
/**
* Method: checkImgURL
* Make sure that the image that just loaded is the one this tile is meant
* to display, since panning/zooming might have changed the tile's URL in
* the meantime. If the tile URL did change before the image loaded, set
* the imgDiv display to 'none', as either (a) it will be reset to visible
* when the new URL loads in the image, or (b) we don't want to display
* this tile after all because its new bounds are outside our maxExtent.
*
* This function should no longer be neccesary with the improvements to
* Grid.js in OpenLayers 2.3. The lack of a good isEquivilantURL function
* caused problems in 2.2, but it's possible that with the improved
* isEquivilant URL function, this might be neccesary at some point.
*
* See discussion in the thread at
* http://openlayers.org/pipermail/dev/2007-January/000205.html
*/
checkImgURL: function () {
// Sometimes our image will load after it has already been removed
// from the map, in which case this check is not needed.
if (this.layer) {
var loaded = this.layerAlphaHack ? this.imgDiv.firstChild.src : this.imgDiv.src;
if (!OpenLayers.Util.isEquivalentUrl(loaded, this.url)) {
this.hide();
}
}
},
/**
* Method: startTransition
* This method is invoked on tiles that are backBuffers for tiles in the
* grid. The grid tile is about to be cleared and a new tile source
* loaded. This is where the transition effect needs to be started
* to provide visual continuity.
*/
startTransition: function() {
// backBufferTile has to be valid and ready to use
if (!this.backBufferTile || !this.backBufferTile.imgDiv) {
return;
}
// calculate the ratio of change between the current resolution of the
// backBufferTile and the layer. If several animations happen in a
// row, then the backBufferTile will scale itself appropriately for
// each request.
var ratio = 1;
if (this.backBufferTile.resolution) {
ratio = this.backBufferTile.resolution / this.layer.getResolution();
}
// if the ratio is not the same as it was last time (i.e. we are
// zooming), then we need to adjust the backBuffer tile
if (ratio != 1) {
if (this.layer.transitionEffect == 'resize') {
// In this case, we can just immediately resize the
// backBufferTile.
var upperLeft = new OpenLayers.LonLat(
this.backBufferTile.bounds.left,
this.backBufferTile.bounds.top
);
var size = new OpenLayers.Size(
this.backBufferTile.size.w * ratio,
this.backBufferTile.size.h * ratio
);
var px = this.layer.map.getLayerPxFromLonLat(upperLeft);
OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame,
null, px, size);
var imageSize = this.backBufferTile.imageSize;
imageSize = new OpenLayers.Size(imageSize.w * ratio,
imageSize.h * ratio);
var imageOffset = this.backBufferTile.imageOffset;
if(imageOffset) {
imageOffset = new OpenLayers.Pixel(
imageOffset.x * ratio, imageOffset.y * ratio
);
}
OpenLayers.Util.modifyDOMElement(
this.backBufferTile.imgDiv, null, imageOffset, imageSize
) ;
this.backBufferTile.show();
}
} else {
// default effect is just to leave the existing tile
// until the new one loads if this is a singleTile and
// there was no change in resolution. Otherwise we
// don't bother to show the backBufferTile at all
if (this.layer.singleTile) {
this.backBufferTile.show();
} else {
this.backBufferTile.hide();
}
}
},
/**
* Method: show
* Show the tile by showing its frame.
*/
show: function() {
this.frame.style.display = '';
// Force a reflow on gecko based browsers to actually show the element
// before continuing execution.
if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,
this.layer.transitionEffect) != -1) {
if (OpenLayers.IS_GECKO === true) {
this.frame.scrollLeft = this.frame.scrollLeft;
}
}
},
/**
* Method: hide
* Hide the tile by hiding its frame.
*/
hide: function() {
this.frame.style.display = 'none';
},
CLASS_NAME: "OpenLayers.Tile.Image"
}
);
OpenLayers.Tile.Image.useBlankTile = (
OpenLayers.BROWSER_NAME == "safari" ||
OpenLayers.BROWSER_NAME == "opera");