OpenLayers OpenLayers

Ticket #933: transition-resize.patch

File transition-resize.patch, 20.8 kB (added by pspencer, 1 year ago)

patch to add transition effect support framework and a resize effect.

  • tests/list-tests.html

    old new  
    11<ul id="testlist"> 
     2    <li>test_Ajax.html</li> 
    23    <li>test_OpenLayers.html</li> 
    34    <li>test_Ajax.html</li> 
    45    <li>test_BaseTypes.html</li> 
  • lib/OpenLayers/Tile/Image.js

    old new  
    7777     */ 
    7878    destroy: function() { 
    7979        if (this.imgDiv != null)  { 
    80             OpenLayers.Event.stopObservingElement(this.imgDiv.id); 
     80            if (this.layer.alpha) { 
     81                OpenLayers.Event.stopObservingElement(this.imgDiv.childNodes[0].id);                 
     82            }else { 
     83                OpenLayers.Event.stopObservingElement(this.imgDiv.id); 
     84            } 
    8185            if (this.imgDiv.parentNode == this.frame) { 
    8286                this.frame.removeChild(this.imgDiv); 
    8387                this.imgDiv.map = null; 
     
    117121             
    118122         
    119123        return obj; 
    120     }, 
     124    },     
    121125     
     126     
    122127    /** 
    123128     * Method: draw 
    124129     * Check that a tile should be drawn, and draw it. 
     
    126131     * Returns: 
    127132     * {Boolean} Always returns true. 
    128133     */ 
    129     draw:function() { 
     134    draw: function() { 
    130135        if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { 
    131136            this.bounds = this.getBoundsFromBaseLayer(this.position); 
    132137        } 
     
    142147            this.events.triggerEvent("loadstart"); 
    143148        } 
    144149         
     150        this.renderTile(); 
     151         
     152        return true; 
     153    }, 
     154     
     155    /** 
     156     * Method: renderTile 
     157     * 
     158     * internal function to actually initialize the image tile, 
     159     * position it correctly, and set its url 
     160     */ 
     161    renderTile: function() { 
    145162        if (this.imgDiv == null) { 
    146163            this.initImgDiv(); 
    147164        } 
    148165 
    149166        this.imgDiv.viewRequestID = this.layer.map.viewRequestID; 
    150          
    151         this.url = this.layer.getURL(this.bounds); 
    152167        // position the frame  
    153168        OpenLayers.Util.modifyDOMElement(this.frame,  
    154169                                         null, this.position, this.size);    
    155  
     170                                          
     171        this.url = this.layer.getURL(this.bounds); 
    156172        var imageSize = this.layer.getImageSize();  
    157173        if (this.layerAlphaHack) { 
    158174            OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv, 
    159175                    null, null, imageSize, this.url); 
    160176        } else { 
    161             this.imgDiv.src = this.url; 
    162177            OpenLayers.Util.modifyDOMElement(this.imgDiv, 
    163178                    null, null, imageSize) ; 
     179            this.imgDiv.src = this.url; 
    164180        } 
    165         return true; 
    166181    }, 
    167182 
    168183    /**  
     
    172187     */ 
    173188    clear: function() { 
    174189        if(this.imgDiv) { 
    175             this.imgDiv.style.display = "none"; 
     190            this.hide(); 
     191            //this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif"; 
    176192            if (OpenLayers.Tile.Image.useBlankTile) {  
    177193                this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif"; 
    178194            }     
     
    184200     * Creates the imgDiv property on the tile. 
    185201     */ 
    186202    initImgDiv: function() { 
    187          
    188203        var offset = this.layer.imageOffset;  
    189204        var size = this.layer.getImageSize();  
    190       
    191205        if (this.layerAlphaHack) { 
    192206            this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null, 
    193207                                                           offset, 
     
    219233        OpenLayers.Event.observe( this.imgDiv, "load", 
    220234            OpenLayers.Function.bind(this.checkImgURL, this) ); 
    221235        */ 
     236        this.frame.style.zIndex = this.isBackBuffer ? 0 : 1; 
    222237        this.frame.appendChild(this.imgDiv);  
    223238        this.layer.div.appendChild(this.frame);  
    224239 
     
    231246 
    232247        // we need this reference to check back the viewRequestID 
    233248        this.imgDiv.map = this.layer.map; 
    234  
     249         
    235250        //bind a listener to the onload of the image div so that we  
    236251        // can register when a tile has finished loading. 
    237252        var onload = function() { 
    238              
     253         
    239254            //normally isLoading should always be true here but there are some  
    240255            // right funky conditions where loading and then reloading a tile 
    241256            // with the same url *really*fast*. this check prevents sending  
     
    245260                this.isLoading = false;  
    246261                this.events.triggerEvent("loadend");  
    247262            } 
     263 
    248264        }; 
    249         OpenLayers.Event.observe(this.imgDiv, 'load', 
    250                                  OpenLayers.Function.bind(onload, this)); 
     265         
     266        if (this.layer.alphaHack) {  
     267            OpenLayers.Event.observe(this.imgDiv.childNodes[0], 'load',  
     268                                     OpenLayers.Function.bind(onload, this));     
     269        } else {  
     270            OpenLayers.Event.observe(this.imgDiv, 'load',  
     271                                 OpenLayers.Function.bind(onload, this));  
     272        }  
     273         
    251274 
    252275        // Bind a listener to the onerror of the image div so that we 
    253276        // can registere when a tile has finished loading with errors. 
     
    289312        if (this.layer) { 
    290313            var loaded = this.layerAlphaHack ? this.imgDiv.firstChild.src : this.imgDiv.src; 
    291314            if (!OpenLayers.Util.isEquivalentUrl(loaded, this.url)) { 
    292                 this.imgDiv.style.display = "none"
     315                this.hide()
    293316            } 
    294317        } 
    295318    }, 
     319     
     320    /** 
     321     * Method: startTransition 
     322     * 
     323     * This method is invoked on tiles that are backBuffers for tiles in the 
     324     * grid.  The grid tile is about to be cleared and a new tile source 
     325     * loaded.  This is where the transition effect needs to be started 
     326     * to provide visual continuity. 
     327     */ 
     328    startTransition: function() { 
     329        // backBufferTile has to be valid and ready to use 
     330        if (!this.backBufferTile || !this.backBufferTile.imgDiv) { 
     331            return; 
     332        } 
    296333 
     334        // show the backBufferTile and hide this tile 
     335        this.hide(); 
     336 
     337        // calculate the ratio of change between the current resolution of the 
     338        // backBufferTile and the layer.  If several animations happen in a row, 
     339        // then the backBufferTile will scale itself appropriately for each 
     340        // request. 
     341        var ratio = 1; 
     342        if (this.backBufferTile.resolution) { 
     343            ratio = this.backBufferTile.resolution / this.layer.getResolution() ; 
     344        } 
     345         
     346        // if the ratio is not 1 (i.e. we are zooming), then we might be  
     347        // resizing the backBuffer tile ... 
     348        if (ratio != 1) { 
     349            if (this.layer.transitionEffect == 'resize') { 
     350                // In this case, we can just immediately resize the  
     351                // backBufferTile. 
     352                var upperLeft =  
     353                    new OpenLayers.LonLat(this.backBufferTile.bounds.left,  
     354                                          this.backBufferTile.bounds.top); 
     355                var size =  
     356                    new OpenLayers.Size(this.backBufferTile.size.w * ratio,  
     357                                        this.backBufferTile.size.h * ratio); 
     358 
     359                var px = this.layer.map.getLayerPxFromLonLat(upperLeft); 
     360                OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame,  
     361                                                 null, px, size);    
     362                var imageSize = this.backBufferTile.size.clone(); 
     363                imageSize = new OpenLayers.Size(imageSize.w * ratio,  
     364                                                imageSize.h * ratio); 
     365 
     366                OpenLayers.Util.modifyDOMElement(this.backBufferTile.imgDiv, 
     367                                                 null, null, imageSize) ; 
     368 
     369                this.backBufferTile.show(); 
     370            } 
     371        } else { 
     372            // default effect is just to leave the existing tile 
     373            // until the new one loads if this is a singleTile and 
     374            // there was no change in resolution.  Otherwise we 
     375            // don't bother to show the backBufferTile at all 
     376            if (this.layer.singleTile && ratio == 1) { 
     377                this.backBufferTile.show(); 
     378            } else { 
     379                this.backBufferTile.hide(); 
     380            } 
     381        } 
     382    }, 
     383     
     384    /**  
     385     * Method: show 
     386     * 
     387     * show the tile by showing its frame. 
     388     */ 
     389    show: function() { 
     390        this.frame.style.display = ''; 
     391    }, 
     392     
     393    /**  
     394     * Method: hide 
     395     * 
     396     * hide the tile by hiding its frame. 
     397     */ 
     398    hide: function() { 
     399        this.frame.style.display = 'none'; 
     400    }, 
     401     
     402    /** @final @type String */ 
    297403    CLASS_NAME: "OpenLayers.Tile.Image" 
    298404  } 
    299405); 
  • lib/OpenLayers/Tile.js

    old new  
    8080     */ 
    8181    isLoading: false, 
    8282     
     83    /** 
     84     * Property: isBackBuffer 
     85     * {Boolean} Is this tile a back buffer tile? 
     86     */ 
     87    isBackBuffer: false, 
     88     
     89    /** 
     90     * Property: isFirstDraw 
     91     * {Boolean} Is this the first time the tile is being drawn? 
     92     * This is used to force resetBackBuffer to synchronize 
     93     * the backBufferTile with the foreground tile the first time 
     94     * the foreground tile loads so that if the user zooms 
     95     * before the layer has fully loaded, the backBufferTile for 
     96     * tiles that have been loaded can be used. 
     97     */ 
     98    isFirstDraw: true, 
     99         
     100    /** 
     101     * Property: backBufferTile 
     102     * {<OpenLayers.Tile>} A clone of the tile used to create transition effects 
     103     * when the tile is moved or changes resolution. 
     104     */ 
     105    backBufferTile: null, 
     106         
    83107    /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor. 
    84108     *             there is no need for the base tile class to have a url. 
    85109     *  
     
    111135     * Nullify references to prevent circular references and memory leaks. 
    112136     */ 
    113137    destroy:function() { 
     138        this.layer.events.unregister("loadend", this, this.resetBackBuffer); 
     139        this.events.unregister('loadend', this, this.resetBackBuffer); 
    114140        this.layer  = null; 
    115141        this.bounds = null; 
    116142        this.size = null; 
     
    118144         
    119145        this.events.destroy(); 
    120146        this.events = null; 
     147         
     148        /* clean up the backBufferTile if it exists */ 
     149        if (this.backBufferTile) { 
     150            this.backBufferTile.destroy(); 
     151            this.backBufferTile = null; 
     152        } 
    121153    }, 
    122154     
     155      
    123156    /** 
    124157     * Method: clone 
    125158     * 
     
    157190     */ 
    158191    draw: function() { 
    159192         
    160         //clear tile's contents and mark as not drawn 
    161         this.clear(); 
    162          
    163193        var maxExtent = this.layer.maxExtent; 
    164194        var withinMaxExtent = (maxExtent && 
    165195                               this.bounds.intersectsBounds(maxExtent, false)); 
    166196  
    167197        // The only case where we *wouldn't* want to draw the tile is if the  
    168198        // tile is outside its layer's maxExtent. 
    169         return (withinMaxExtent || this.layer.displayOutsideMaxExtent); 
     199        var drawTile = (withinMaxExtent || this.layer.displayOutsideMaxExtent); 
     200         
     201        if (drawTile) { 
     202            //we use a clone of this tile to create a double buffer for visual 
     203            //continuity.  The backBufferTile is used to create transition 
     204            //effects while the tile in the grid is repositioned and redrawn 
     205            if (!this.backBufferTile) { 
     206                this.backBufferTile = this.clone(); 
     207                this.backBufferTile.hide(); 
     208                // this is important.  It allows the backBuffer to place itself 
     209                // appropriately in the DOM.  The Image subclass needs to put 
     210                // the backBufferTile behind the main tile so the tiles can 
     211                // load over top and display as soon as they are loaded. 
     212                this.backBufferTile.isBackBuffer = true; 
     213                // potentially end any transition effects when the tile loads 
     214                this.events.register('loadend', this, this.resetBackBuffer); 
     215                // clear transition back buffer tile only after all tiles in this 
     216                // layer have loaded to avoid visual glitches 
     217                this.layer.events.register("loadend", this, this.resetBackBuffer); 
     218            } 
     219            // run any transition effects 
     220            this.startTransition(); 
     221        } else { 
     222            // if we aren't going to draw the tile, then the backBuffer should 
     223            // be hidden too! 
     224            if (this.backBufferTile) { 
     225                this.backBufferTile.clear(); 
     226            } 
     227        } 
     228        this.shouldDraw = drawTile; 
     229         
     230        //clear tile's contents and mark as not drawn 
     231        this.clear(); 
     232         
     233        return drawTile; 
    170234    }, 
    171235     
    172236    /**  
     
    240304                                       topLeft.lat);   
    241305        return bounds; 
    242306    },         
    243  
     307     
     308    /**  
     309     * Method: startTransition 
     310     * 
     311     * prepare the tile for a transition effect.  To be 
     312     * implemented by subclasses. 
     313     */ 
     314    startTransition: function() {}, 
     315     
     316    /**  
     317     * Method: resetBackBuffer 
     318     * 
     319     * triggered by two different events, layer loadend, and tile loadend. 
     320     * In any of these cases, we check to see if we can hide the  
     321     * backBufferTile yet and update its parameters to match the  
     322     * foreground tile.  Basic logic: 
     323     * 
     324     * - If the backBufferTile hasn't been drawn yet, reset it 
     325     * - If layer is still loading, show foreground tile but don't hide 
     326     *   the backBufferTile yet 
     327     * - If layer is done loading, reset backBuffer tile and show  
     328     *   foreground tile 
     329     */ 
     330    resetBackBuffer: function(args) { 
     331        if (this.backBufferTile &&  
     332            (this.isFirstDraw || !this.layer.numLoadingTiles)) { 
     333            this.backBufferTile.hide(); 
     334            this.backBufferTile.position = this.position.clone(); 
     335            this.backBufferTile.bounds = this.bounds.clone(); 
     336            this.backBufferTile.size = this.size.clone(); 
     337            this.backBufferTile.resolution = this.layer.getResolution(); 
     338            this.backBufferTile.renderTile(); 
     339            this.isFirstDraw = false; 
     340        } 
     341        if (this.shouldDraw) { 
     342            this.show(); 
     343        } 
     344    }, 
     345         
     346    /**  
     347     * Method: show 
     348     * 
     349     * show the tile.  To be implemented by subclasses. 
     350     */ 
     351    show: function() { }, 
     352     
     353    /**  
     354     * Method: hide 
     355     * 
     356     * hide the tile.  To be implemented by subclasses. 
     357     */ 
     358    hide: function() { }, 
     359     
    244360    CLASS_NAME: "OpenLayers.Tile" 
    245361}); 
  • lib/OpenLayers/Layer.js

    old new  
    231231     */ 
    232232    wrapDateLine: false, 
    233233     
     234    /** 
     235     * APIProperty: transitionEffect 
     236     * {String} The transition effect to use when the map is panned or 
     237     * zoomed.  There is currently two supported values: 
     238     * null - no transition effect (the default). 
     239     * resize - existing tiles are resized on zoom to provide a visual 
     240     *   effect of the zoom having taken place immediately.  As the 
     241     *   new tiles become available, they are drawn over top of the 
     242     *    resized tiles. 
     243     */ 
     244    transitionEffect: null, 
    234245     
    235246    /** 
    236247     * Constructor: OpenLayers.Layer 
  • lib/OpenLayers/Layer/Grid.js

    old new  
    5959     */ 
    6060    numLoadingTiles: 0, 
    6161 
     62     /**                                                  
     63     * APIProperty: gridOrigin                               
     64     * {LongLat} origin of the grid tiles for all resolution. Usefull for WMSC 
     65     * layers which requested tiles must be stricly aligned to the server grid 
     66     * If not specified : bottom left corner of the map.maxExtent 
     67     */                                                 
     68    gridOrigin: null,  
     69 
    6270    /** 
    6371     * Constructor: OpenLayers.Layer.Grid 
    6472     * Create a new grid layer 
     
    316324        var minCols = Math.ceil(viewSize.w/this.tileSize.w) + 
    317325                      Math.max(1, 2 * this.buffer); 
    318326         
    319         var extent = this.map.getMaxExtent(); 
     327        this.gridOrigin = this.gridOrigin || new OpenLayers.LonLat( 
     328            this.map.getMaxExtent().left, this.map.getMaxExtent().bottom); 
     329 
    320330        var resolution = this.map.getResolution(); 
    321331        var tilelon = resolution * this.tileSize.w; 
    322332        var tilelat = resolution * this.tileSize.h; 
    323333         
    324         var offsetlon = bounds.left - extent.left
     334        var offsetlon = bounds.left - this.gridOrigin.lon
    325335        var tilecol = Math.floor(offsetlon/tilelon) - this.buffer; 
    326336        var tilecolremain = offsetlon/tilelon - tilecol; 
    327337        var tileoffsetx = -tilecolremain * this.tileSize.w; 
    328         var tileoffsetlon = extent.left + tilecol * tilelon; 
     338        var tileoffsetlon = this.gridOrigin.lon + tilecol * tilelon; 
    329339         
    330         var offsetlat = bounds.top - (extent.bottom + tilelat);   
     340        var offsetlat = bounds.top - (this.gridOrigin.lat + tilelat);   
    331341        var tilerow = Math.ceil(offsetlat/tilelat) + this.buffer; 
    332342        var tilerowremain = tilerow - offsetlat/tilelat; 
    333343        var tileoffsety = -tilerowremain * this.tileSize.h; 
    334         var tileoffsetlat = extent.bottom + tilerow * tilelat; 
     344        var tileoffsetlat = this.gridOrigin.lat + tilerow * tilelat; 
    335345         
    336346        tileoffsetx = Math.round(tileoffsetx); // heaven help us 
    337347        tileoffsety = Math.round(tileoffsety); 
  • examples/transition.html

    old new  
     1<html xmlns="http://www.w3.org/1999/xhtml"> 
     2  <head> 
     3    <style type="text/css"> 
     4        #mapDiv { 
     5            width: 400px; 
     6            height: 400px; 
     7            border: 1px solid black; 
     8        } 
     9    </style> 
     10    <script src="../lib/OpenLayers.js"></script> 
     11    <script type="text/javascript"> 
     12        var map; 
     13        function init(){ 
     14            map = new OpenLayers.Map('mapDiv', {maxResolution: 'auto'}); 
     15 
     16            var single_default_effect = new OpenLayers.Layer.WMS( "WMS untiled default",  
     17                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'},  
     18                { singleTile: true, isBaseLayer: true} ); 
     19            var single_resize_effect = new OpenLayers.Layer.WMS( "WMS untiled resize",  
     20                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'},  
     21                { singleTile: true, isBaseLayer: true, transitionEffect: 'resize'} ); 
     22            var tiled_default_effect = new OpenLayers.Layer.WMS( "WMS tiled default ",  
     23                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'},  
     24                { isBaseLayer: true} ); 
     25            var tiled_resize_effect = new OpenLayers.Layer.WMS( "WMS tiled resize",  
     26                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'},  
     27                { isBaseLayer: true, transitionEffect: 'resize'} ); 
     28 
     29            map.addLayers([single_default_effect, single_resize_effect, 
     30                           tiled_default_effect, tiled_resize_effect]); 
     31            map.addControl(new OpenLayers.Control.LayerSwitcher()); 
     32            map.setCenter(new OpenLayers.LonLat(6.5, 40.5), 4); 
     33        } 
     34    </script> 
     35  </head> 
     36  <body onload="init()"> 
     37    <h1 id="title">Transition Example</h1> 
     38    <p id="shortdesc"> 
     39      Demonstrates the use of transition effects in tiled and untiled layers.  There are two transitions that are currently implemented, 'default', 'resize'.  The default transition effect is used when no transition is specified and is implemented as no transition effect except for panning singleTile layers.  The 'resize' effect resamples the current tile and displays it stretched or compressed until the new tile is available.</p>   
     40    <div id="mapDiv"></div> 
     41    <p> The first layer is an untiled WMS layer with no transition effect. </p> 
     42    <p> The second layer is an untiled WMS layer with a 'resize' effect. </p> 
     43    <p> The third layer is a tiled WMS layer with no transition effect. </p> 
     44    <p> The fourth layer is a tiled WMS layer with a 'resize' effect. </p> 
     45  </body> 
     46</html>