OpenLayers OpenLayers

Ticket #1357: ordering.diff

File ordering.diff, 16.3 kB (added by tcoulter, 7 months ago)
  • OpenLayers/Renderer/Elements.js

    old new  
    77 */ 
    88 
    99/** 
     10 * Class: OpenLayers.ElementsIndexer 
     11 * This class takes care of figuring out which order elements should be 
     12 * placed in the DOM based on given indexing methods.  
     13 */ 
     14OpenLayers.ElementsIndexer = OpenLayers.Class({ 
     15    
     16    /** 
     17     * Property: maxZIndex 
     18     * {Integer} This is the largest-most z-index value for a node 
     19     * contained within the indexer. 
     20     */ 
     21    maxZIndex: null, 
     22     
     23    /** 
     24     * Property: order 
     25     * {Array<String>} This is an array of node id's stored in the 
     26     * order that they should show up on screen. Id's higher up in the 
     27     * array (higher array index) represent nodes with higher z-indeces. 
     28     */ 
     29    order: null,  
     30     
     31    /** 
     32     * Property: indices 
     33     * {Hash} This is a hash that maps node ids to their z-index value 
     34     * stored in the indexer. This is done to make finding a nodes z-index  
     35     * value O(1). 
     36     */ 
     37    indices: null, 
     38     
     39    /** 
     40     * Property: compare 
     41     * {Function} This is the function used to determine placement of 
     42     * of a new node within the indexer. If null, this defaults to to 
     43     * the Z_ORDER_DRAWING_ORDER comparison method. 
     44     */ 
     45    compare: null, 
     46    
     47    /** 
     48     * APIMethod: initialize 
     49     * Create a new indexer with  
     50     *  
     51     * Parameters: 
     52     * ordering - {Function} The canonical name of the indexing method to 
     53     * use. See OpenLayers.ElementsIndexer.Translations below. If null,  
     54     * this defaults to "z". 
     55     */ 
     56    initialize: function(ordering) { 
     57         
     58        var compareMethod = OpenLayers.ElementsIndexer.Translations[ordering]; 
     59         
     60        this.compare = compareMethod || OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; 
     61        this.order = []; 
     62        this.indices = {}; 
     63        this.maxZIndex = 0; 
     64    }, 
     65     
     66    /** 
     67     * APIMethod: insert 
     68     * Insert a new node into the indexer. In order to find the correct positioning for 
     69     * the node to be inserted, this method uses a binary search. This makes inserting 
     70     * O(log(n)).  
     71     *  
     72     * Parameters: 
     73     * newNode - The new node to be inserted. 
     74     */ 
     75    insert: function(newNode) { 
     76        var nodeId = this.getNodeId(newNode); 
     77         
     78        this.determineZIndex(newNode);        
     79 
     80        var leftIndex = -1; 
     81        var rightIndex = this.order.length; 
     82        var middle; 
     83 
     84        while (rightIndex - leftIndex > 1) { 
     85            middle = parseInt((leftIndex + rightIndex) / 2); 
     86             
     87            var nextId = this.order[middle]; 
     88            var next = OpenLayers.Util.getElement(nextId); 
     89             
     90            var placement = this.compare(this, newNode, next); 
     91             
     92            if (placement > 0) { 
     93                leftIndex = middle; 
     94            } else { 
     95                rightIndex = middle; 
     96            }  
     97        } 
     98         
     99        this.order.splice(rightIndex, 0, nodeId); 
     100        this.indices[nodeId] = this.getZIndex(newNode); 
     101         
     102        // Return the next item in the array -- the id of the node that should 
     103        // follow this one directly in the index order -- if any. 
     104        var nextIndex = rightIndex + 1; 
     105         
     106        var returnVal = null; 
     107        if (nextIndex > 0 && nextIndex < this.order.length) { 
     108            returnVal = this.order[nextIndex]; 
     109        } 
     110        return returnVal; 
     111    }, 
     112     
     113    remove: function(node) { 
     114        var nodeId = this.getNodeId(node); 
     115        var arrayIndex = this.order.indexOf(nodeId); 
     116        if (arrayIndex >= 0) { 
     117            // Remove it from the order array, as well as deleting the node 
     118            // from the indeces hash. 
     119            this.order.splice(arrayIndex, 1); 
     120            delete this.indices[nodeId]; 
     121             
     122            // Reset the maxium z-index based on the last item in the order array. 
     123            var lastId = this.order[this.order.length - 1]; 
     124            this.maxZIndex = this.indices[lastId]; 
     125        } 
     126    }, 
     127     
     128    exists: function(node) { 
     129        var nodeId = this.getNodeId(node); 
     130        return this.indices[nodeId] != null;   
     131    }, 
     132     
     133    /** 
     134     * APIMethod: getZIndex 
     135     * Get the z-index value for the current node from the node data itself. 
     136     */ 
     137    getZIndex: function(node) { 
     138        return node._style.graphicZIndex;   
     139    }, 
     140 
     141    /** 
     142     * APIMethod: getMaxZIndex 
     143     * Get the maximum z-index stored within the indexer. 
     144     */ 
     145    getMaxZIndex: function() { 
     146        return this.maxZIndex; 
     147    }, 
     148     
     149    /** 
     150     * APIMethod: setZIndex 
     151     * Set the z-index for the given node. 
     152     */ 
     153    setZIndex: function(node, zIndex) { 
     154        node._style.graphicZIndex = zIndex;   
     155    }, 
     156     
     157    /** 
     158     * Method: determineZIndex 
     159     * Determine the z-index for the current node if there isn't one,  
     160     * and set the maximum value if we've found a new maximum. 
     161     */ 
     162    determineZIndex: function(node) { 
     163        var zIndex = node._style.graphicZIndex; 
     164         
     165        // Everything must have a zIndex. If none is specified, 
     166        // this means the user *must* (hint: assumption) want this 
     167        // node to succomb to drawing order. To enforce drawing order 
     168        // over all indexing methods, we'll create a new z-index that's 
     169        // greater than any currently in the indexer. 
     170        if (zIndex == null) { 
     171            zIndex = this.maxZIndex; 
     172        } 
     173         
     174        if (zIndex > this.maxZIndex) { 
     175            this.maxZIndex = zIndex; 
     176        } 
     177         
     178        node._style.graphicZIndex = zIndex; 
     179    }, 
     180     
     181    /** 
     182     * Method: getNodeId 
     183     * Get the id of the node passed in. 
     184     */ 
     185    getNodeId: function(node) { 
     186        return node.id;   
     187    }, 
     188    
     189    CLASS_NAME: "OpenLayers.ElementsIndexer" 
     190}); 
     191 
     192 
     193// These are the compare methods for figuring out where a new 
     194// node should be placed within the indexer. These methods are very  
     195// similar to general sorting methods in that they return -1, 0, and 1  
     196// to specify the direction in which new nodes fall in the ordering. 
     197OpenLayers.ElementsIndexer.IndexingMethods = { 
     198    /** 
     199     * Method: Z_ORDER 
     200     * This compare method is used by other comparison methods. 
     201     * It can be used individually for ordering, but is not recommended, 
     202     * because it doesn't subscribe to drawing order. 
     203     */     
     204    Z_ORDER: function(indexer, newNode, next) { 
     205        var newZIndex = indexer.getZIndex(newNode); 
     206 
     207        var returnVal = 0; 
     208        if (next) { 
     209            var nextZIndex = indexer.getZIndex(next); 
     210            returnVal = newZIndex - nextZIndex;  
     211        } 
     212         
     213        return returnVal; 
     214    }, 
     215    /** 
     216     * API Method: Z_ORDER_DRAWING_ORDER 
     217     * This method orders nodes by their z-index, but does so in a way 
     218     * that, if there are other nodes with the same z-index, the newest drawn 
     219     * will be the front most within that z-index. This is the default 
     220     * indexing method. 
     221     */ 
     222    Z_ORDER_DRAWING_ORDER: function(indexer, newNode, next) { 
     223        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, next); 
     224         
     225        // Make Z_ORDER subscribe to drawing order by pushing it above 
     226        // all of the other nodes with the same z-index. 
     227        if (next && returnVal == 0) { 
     228            returnVal = 1; 
     229        } 
     230         
     231        return returnVal; 
     232    }, 
     233    /** 
     234     * API Method: Z_ORDER_Y_ORDER 
     235     * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it 
     236     * best describes which ordering methods have precedence (though, the name would 
     237     * be too long). This method orders nodes by their z-index, but does so in a way 
     238     * that, if there are other nodes with the same z-index, the nodes with the 
     239     * lower y position will be "closer" than those with a higher y position.  
     240     * If two nodes have the exact same y position, however, then this method 
     241     * will revert to using drawing order to decide placement. 
     242     */ 
     243    Z_ORDER_Y_ORDER: function(indexer, newNode, next) { 
     244        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, next); 
     245         
     246        if (next && returnVal == 0) { 
     247            var newLat = newNode._geometry.getBounds().bottom; 
     248            var nextLat = next._geometry.getBounds().bottom; 
     249             
     250            var result = nextLat - newLat; 
     251             
     252            if (result == 0) { 
     253                returnVal = 1; 
     254            } else { 
     255                returnVal = result; 
     256            } 
     257        } 
     258         
     259        return returnVal;        
     260    } 
     261} 
     262 
     263/** 
     264 * Translate canonical names for ordering methods to their  
     265 * function equivalents.  
     266 */ 
     267OpenLayers.ElementsIndexer.Translations = { 
     268    "z" : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER, 
     269    "y" : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER    
     270} 
     271 
     272/** 
    10273 * Class: OpenLayers.Renderer.Elements 
    11274 * This is another virtual class in that it should never be instantiated by  
    12275 *  itself as a Renderer. It exists because there is *tons* of shared  
     
    42305    xmlns: null, 
    43306     
    44307    /** 
     308     * Property: Indexer 
     309     * An instance of OpenLayers.ElementsIndexer created upon 
     310     * initialization. 
     311     */ 
     312    indexer: null,  
     313     
     314    /** 
    45315     * Constructor: OpenLayers.Renderer.Elements 
    46316     *  
    47317     * Parameters: 
    48318     * containerID - {String} 
     319     * ordering - {String} The cononical name for the ordering method 
     320     * used (such as "z"). See OpenLayers.ElementsIndexer.Translations above.  
    49321     */ 
    50     initialize: function(containerID) { 
     322    initialize: function(containerID, ordering) { 
    51323        OpenLayers.Renderer.prototype.initialize.apply(this, arguments); 
    52324 
    53325        this.rendererRoot = this.createRenderRoot(); 
     
    55327         
    56328        this.rendererRoot.appendChild(this.root); 
    57329        this.container.appendChild(this.rendererRoot); 
     330         
     331        this.indexer = new OpenLayers.ElementsIndexer(ordering); 
    58332    }, 
    59333     
    60334    /** 
     
    124398            return; 
    125399        }; 
    126400 
     401        var id = geometry.id; 
     402 
    127403        if (style.display != "none") { 
    128             //first we create the basic node and add it to the root 
    129             var nodeType = this.getNodeType(geometry, style); 
    130             var node = this.nodeFactory(geometry.id, nodeType); 
    131             node._featureId = featureId; 
    132             node._geometryClass = geometry.CLASS_NAME; 
    133             node._style = style; 
    134              
    135             //now actually draw the node, and style it 
    136             node = this.drawGeometryNode(node, geometry); 
    137              
    138             // append the node to root (but only if it's new) 
    139             if (node.parentNode != this.root) {  
    140                 this.root.appendChild(node);  
    141             } 
    142             this.postDraw(node); 
     404            this.redrawNode(id, geometry, style, featureId);  
    143405        } else { 
    144             node = OpenLayers.Util.getElement(geometry.id); 
     406            node = OpenLayers.Util.getElement(id); 
    145407            if (node) { 
    146408                node.parentNode.removeChild(node); 
    147409            } 
    148410        } 
    149411    }, 
     412     
     413    redrawNode: function(id, geometry, style, featureId) { 
     414        // Get the node if it's already on the map. 
     415        var currentNode = OpenLayers.Util.getElement(id); 
     416         
     417        // Create a new node, or use the current one if it's 
     418        // already there. 
     419        var newNode; 
     420        if (!currentNode) { 
     421            var nodeType = this.getNodeType(geometry, style); 
     422            newNode = this.createNode(nodeType, id); 
     423        } else { 
     424            newNode = currentNode; 
     425        } 
     426         
     427        // Set the data for the node, then draw it. 
     428        newNode._featureId = featureId; 
     429        newNode._geometry = geometry; 
     430        newNode._geometryClass = geometry.CLASS_NAME; 
     431        newNode._style = style; 
     432        newNode = this.drawGeometryNode(newNode, geometry, style); 
     433         
     434        // If the node is known to the indexer, remove it so we can 
     435        // recalculate where it should go. 
     436        if (this.indexer.exists(newNode)) { 
     437            this.indexer.remove(newNode); 
     438        } 
     439         
     440        // Insert the node into the indexer so it can show us where to place it. 
     441        // Note that this operation is O(log(n)). If there's a performance problem 
     442        // (when dragging, for instance) this is likely where it would be. 
     443        var nextNodeId = this.indexer.insert(newNode); 
    150444 
     445        // If the new node should be before another in the index 
     446        // order, insert the new node before the next; else, lets just 
     447        // append the new one on the end, making it the highest in the index order. 
     448        if (nextNodeId) { 
     449            var nextNode = OpenLayers.Util.getElement(nextNodeId); 
     450            this.root.insertBefore(newNode, nextNode); 
     451        } else { 
     452            this.root.appendChild(newNode); 
     453        } 
     454         
     455        this.postDraw(newNode);  
     456    }, 
     457 
    151458    /** 
    152459     * Method: drawGeometryNode 
    153460     * Given a node, draw a geometry on the specified layer. 
     
    328635            var element = OpenLayers.Util.getElement(geometry.id); 
    329636            if (element && element.parentNode) { 
    330637                element.parentNode.removeChild(element); 
     638                this.indexer.remove(element); 
    331639            } 
    332640        } 
    333641    },     
    334  
     642     
    335643    /**  
    336644     * Method: nodeFactory 
    337645     * Create new node of the specified type, with the (optional) specified id. 
  • OpenLayers/Renderer/VML.js

    old new  
    367367     * {DOMElement} The specific render engine's root element 
    368368     */ 
    369369    createRenderRoot: function() { 
    370         return this.nodeFactory(this.container.id + "_vmlRoot", "div"); 
     370        return this.createNode("div", this.container.id + "_vmlRoot"); 
    371371    }, 
    372372 
    373373    /** 
     
    378378     * {DOMElement} The main root element to which we'll add vectors 
    379379     */ 
    380380    createRoot: function() { 
    381         return this.nodeFactory(this.container.id + "_root", "v:group"); 
     381        return this.createNode("v:group", this.container.id + "_root"); 
    382382    }, 
    383383 
    384384    /************************************** 
  • OpenLayers/Renderer/SVG.js

    old new  
    287287     * {DOMElement} The specific render engine's root element 
    288288     */ 
    289289    createRenderRoot: function() { 
    290         return this.nodeFactory(this.container.id + "_svgRoot", "svg"); 
     290        return this.createNode("svg", this.container.id + "_svgRoot"); 
    291291    }, 
    292292 
    293293    /** 
     
    297297     * {DOMElement} The main root element to which we'll add vectors 
    298298     */ 
    299299    createRoot: function() { 
    300         return this.nodeFactory(this.container.id + "_root", "g"); 
     300        return this.createNode("g", this.container.id + "_root"); 
    301301    }, 
    302302 
    303303    /************************************** 
  • OpenLayers/Layer/Vector.js

    old new  
    113113     * {<OpenLayers.Renderer>} 
    114114     */ 
    115115    renderer: null, 
     116     
     117    /** 
     118     * APIProperty: ordering 
     119     * {String} The ordering method that the layer's renderer should use. 
     120     *  
     121     * Valid values are: 
     122     *      "z" - Use z-ordering. 
     123     *      "y" - Use y-ordering. 
     124     *  
     125     * This defaults to z-ordering if no ordering is specified. 
     126     */ 
     127    ordering: "z", 
    116128    
    117129    /**  
    118130     * APIProperty: geometryType 
     
    194206        for (var i = 0; i < this.renderers.length; i++) { 
    195207            var rendererClass = OpenLayers.Renderer[this.renderers[i]]; 
    196208            if (rendererClass && rendererClass.prototype.supported()) { 
    197                this.renderer = new rendererClass(this.div); 
    198                break; 
     209                this.renderer = new rendererClass(this.div, this.ordering); 
     210                break; 
    199211            }   
    200212        }   
    201213    },