OpenLayers OpenLayers

Changeset 4204

Show
Ignore:
Timestamp:
09/10/07 16:10:55 (1 year ago)
Author:
tschaub
Message:

read/write KML - support for all KML 2.1 geometries and all OL geometries

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • sandbox/tschaub/xml/lib/OpenLayers/Format/KML.js

    r4075 r4204  
    66 * @requires OpenLayers/Format.js 
    77 * @requires OpenLayers/Feature/Vector.js 
    8  * @requires OpenLayers/Ajax.js 
     8 * 
     9 * Class: OpenLayers.Format.KML 
     10 * Read/Wite KML. Create a new instance with the <OpenLayers.Format.KML> 
     11 *     constructor.  
    912 *  
    10  * Class: OpenLayers.Format.KML 
    11  * Read only KML. Largely Proof of Concept: does not support advanced Features, 
    12  *     including Polygons.  Create a new instance with the  
    13  *     <OpenLayers.Format.KML> constructor. 
    14  * 
    1513 * Inherits from: 
    16  *  - <OpenLayers.Format
     14 *  - <OpenLayers.Format.XML
    1715 */ 
    18 OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format, { 
     16OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { 
    1917     
    2018    /** 
    2119     * APIProperty: kmlns 
    22      * KML Namespace to use. Defaults to 2.0 namespace. 
     20     * {String} KML Namespace to use. Defaults to 2.0 namespace. 
    2321     */ 
    2422    kmlns: "http://earth.google.com/kml/2.0", 
    2523     
     24    /**  
     25     * APIProperty: placemarksDesc 
     26     * {String} Name of the placemarks.  Default is "No description available." 
     27     */ 
     28    placemarksDesc: "No description available", 
     29     
     30    /**  
     31     * APIProperty: foldersName 
     32     * {String} Name of the folders.  Default is "OpenLayers export." 
     33     */ 
     34    foldersName: "OpenLayers export", 
     35     
     36    /**  
     37     * APIProperty: foldersDesc 
     38     * {String} Description of the folders. Default is "Exported on [date]." 
     39     */ 
     40    foldersDesc: "Exported on " + new Date(), 
     41     
     42    /** 
     43     * APIProperty: extractAttributes 
     44     * {Boolean} Extract attributes from KML.  Default is true. 
     45     */ 
     46    extractAttributes: true, 
     47 
    2648    /** 
    2749     * Constructor: OpenLayers.Format.KML 
    28      * Create a new parser for KML 
     50     * Create a new parser for KML. 
    2951     * 
    3052     * Parameters: 
    3153     * options - {Object} An optional object whose properties will be set on 
    32      *                    this instance. 
     54     *     this instance. 
    3355     */ 
    3456    initialize: function(options) { 
    35         OpenLayers.Format.prototype.initialize.apply(this, [options]); 
     57        // compile regular expressions once instead of every time they are used 
     58        this.regExes = { 
     59            trimSpace: (/^\s*|\s*$/g), 
     60            removeSpace: (/\s*/g), 
     61            splitSpace: (/\s+/), 
     62            trimComma: (/\s*,\s*/g) 
     63        }; 
     64        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); 
    3665    }, 
    3766 
     
    4170     *  
    4271     * Parameters:  
    43      * data - {string} or {XMLNode>} data to read/parse. 
    44      */ 
    45      read: function(data) { 
    46         if (typeof data == "string") {  
     72     * data - {String} or {DOMElement} data to read/parse. 
     73     * 
     74     * Returns: 
     75     * {Array(<OpenLayers.Feature.Vector>)} List of features. 
     76     */ 
     77    read: function(data) { 
     78        if(typeof data == "string") { 
    4779            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); 
    4880        } 
    49         var featureNodes = this.getElementsByTagNameNS(data, this.kmlns, "Placemark"); 
    50          
    51         var features = []; 
    52          
    53         // Process all the featureMembers 
    54         for (var i = 0; i < featureNodes.length; i++) { 
     81        var featureNodes = this.getElementsByTagNameNS(data, 
     82                                                       this.kmlns, 
     83                                                       "Placemark"); 
     84        var features = [];         
     85        for(var i=0; i<featureNodes.length; i++) { 
    5586            var feature = this.parseFeature(featureNodes[i]); 
    56  
    57             if (feature) { 
     87            if(feature) { 
    5888                features.push(feature); 
    5989            } 
     
    6292     }, 
    6393 
    64      /** 
    65       * Method: parseFeature 
    66       * This function is the core of the KML parsing code in OpenLayers. 
    67       * It creates the geometries that are then attached to the returned 
    68       * feature, and calls parseAttributes() to get attribute data out. 
    69       * 
    70       * Parameters: 
    71       * xmlNode - {<DOMElement>}  
    72       */ 
    73      parseFeature: function(xmlNode) { 
     94    /** 
     95     * Method: parseFeature 
     96     * This function is the core of the KML parsing code in OpenLayers. 
     97     *     It creates the geometries that are then attached to the returned 
     98     *     feature, and calls parseAttributes() to get attribute data out. 
     99     * 
     100     * Parameters: 
     101     * node - {<DOMElement>} 
     102     * 
     103     * Returns: 
     104     * {<OpenLayers.Feature.Vector>} A vector feature. 
     105     */ 
     106    parseFeature: function(node) { 
    74107        var geom; 
    75108        var p; // [points,bounds] 
    76109 
    77         var feature = new OpenLayers.Feature.Vector(); 
    78  
    79         // match Point 
    80         if (this.getElementsByTagNameNS(xmlNode, 
    81                                         this.kmlns, "Point").length != 0) { 
    82             var point = this.getElementsByTagNameNS(xmlNode, 
    83                                                     this.kmlns, "Point")[0]; 
    84              
    85             p = this.parseCoords(point); 
    86             if (p.points) { 
    87                 geom = p.points[0]; 
    88                 // TBD Bounds only set for one of multiple geometries 
    89                 geom.extendBounds(p.bounds); 
    90             } 
    91          
    92         // match LineString  
    93         } else if (this.getElementsByTagNameNS(xmlNode, this.kmlns, 
    94                                                "LineString").length != 0) { 
    95             var linestring = this.getElementsByTagNameNS(xmlNode, this.kmlns, 
    96                                                          "LineString")[0]; 
    97             p = this.parseCoords(linestring); 
    98             if (p.points) { 
    99                 geom = new OpenLayers.Geometry.LineString(p.points); 
    100                 // TBD Bounds only set for one of multiple geometries 
    101                 geom.extendBounds(p.bounds); 
    102             } 
    103         } 
    104          
    105         feature.geometry = geom; 
    106         feature.attributes = this.parseAttributes(xmlNode); 
    107          
     110        // only accept one geometry per feature - look for highest "order" 
     111        var order = ["MultiGeometry", "Polygon", "LineString", "Point"]; 
     112        var type, nodeList, geometry, parser; 
     113        for(var i=0; i<order.length; ++i) { 
     114            type = order[i]; 
     115            nodeList = this.getElementsByTagNameNS(node, this.kmlns, type); 
     116            if(nodeList.length > 0) { 
     117                // only deal with first geometry of this type 
     118                var parser = this.parseGeometry[type.toLowerCase()]; 
     119                if(parser) { 
     120                    geometry = parser.apply(this, [nodeList[0]]); 
     121                } else { 
     122                    OpenLayers.Console.error("Unsupported geometry type: " + 
     123                                             type); 
     124                } 
     125                // stop looking for different geometry types 
     126                break; 
     127            } 
     128        } 
     129         
     130        // construct feature (optionally with attributes) 
     131        var attributes; 
     132        if(this.extractAttributes) { 
     133            attributes = this.parseAttributes(node); 
     134        } 
     135        var feature = new OpenLayers.Feature.Vector(geometry, attributes); 
     136 
     137        var fid = node.getAttribute("id"); 
     138        if(fid != null) { 
     139            feature.fid = fid; 
     140        } 
     141 
    108142        return feature; 
    109143    },         
    110144     
    111145    /** 
     146     * Property: parseGeometry 
     147     * Properties of this object are the functions that parse geometries based 
     148     *     on their type. 
     149     */ 
     150    parseGeometry: { 
     151         
     152        /** 
     153         * Method: parseGeometry.point 
     154         * Given a KML node representing a point geometry, create an OpenLayers 
     155         *     point geometry. 
     156         * 
     157         * Parameters: 
     158         * node - {DOMElement} A KML Point node. 
     159         * 
     160         * Returns: 
     161         * {<OpenLayers.Geometry.Point>} A point geometry. 
     162         */ 
     163        point: function(node) { 
     164            var nodeList = this.getElementsByTagNameNS(node, this.kmlns, 
     165                                                       "coordinates"); 
     166            var coords = []; 
     167            if(nodeList.length > 0) { 
     168                var coordString = nodeList[0].firstChild.nodeValue; 
     169                coordString = coordString.replace(this.regExes.removeSpace, ""); 
     170                coords = coordString.split(","); 
     171            } 
     172 
     173            var point = null; 
     174            if(coords.length) { 
     175                // preserve third dimension 
     176                if(coords.length == 2) { 
     177                    coords[2] = null; 
     178                } 
     179                point = new OpenLayers.Geometry.Point(coords[0], coords[1], 
     180                                                      coords[2]); 
     181            } 
     182            return point; 
     183        }, 
     184         
     185        /** 
     186         * Method: parseGeometry.linestring 
     187         * Given a KML node representing a linestring geometry, create an 
     188         *     OpenLayers linestring geometry. 
     189         * 
     190         * Parameters: 
     191         * node - {DOMElement} A KML LineString node. 
     192         * 
     193         * Returns: 
     194         * {<OpenLayers.Geometry.LineString>} A linestring geometry. 
     195         */ 
     196        linestring: function(node, ring) { 
     197            var nodeList = this.getElementsByTagNameNS(node, this.kmlns, 
     198                                                       "coordinates"); 
     199            var coords; 
     200            var numPoints; 
     201            if(nodeList.length > 0) { 
     202                var coordString = nodeList[0].firstChild.nodeValue; 
     203                coordString = coordString.replace(this.regExes.trimSpace, 
     204                                                  ""); 
     205                coordString = coordString.replace(this.regExes.trimComma, 
     206                                                  ","); 
     207                var pointList = coordString.split(this.regExes.splitSpace); 
     208                numPoints = pointList.length; 
     209                var points = new Array(numPoints); 
     210                var numCoords; 
     211                for(var i=0; i<numPoints; ++i) { 
     212                    coords = pointList[i].split(","); 
     213                    numCoords = coords.length; 
     214                    if(numCoords > 1) { 
     215                        if(coords.length == 2) { 
     216                            coords[2] = null; 
     217                        } 
     218                        points[i] = new OpenLayers.Geometry.Point(coords[0], 
     219                                                                  coords[1], 
     220                                                                  coords[2]); 
     221                    } else { 
     222                        points[i] = new OpenLayers.Geometry.Point(); 
     223                    } 
     224                } 
     225            } 
     226 
     227            var line = null; 
     228            if(numPoints) { 
     229                if(ring) { 
     230                    line = new OpenLayers.Geometry.LinearRing(points); 
     231                } else { 
     232                    line = new OpenLayers.Geometry.LineString(points); 
     233                } 
     234            } 
     235            return line; 
     236        }, 
     237         
     238        /** 
     239         * Method: parseGeometry.polygon 
     240         * Given a KML node representing a polygon geometry, create an 
     241         *     OpenLayers polygon geometry. 
     242         * 
     243         * Parameters: 
     244         * node - {DOMElement} A KML Polygon node. 
     245         * 
     246         * Returns: 
     247         * {<OpenLayers.Geometry.Polygon>} A polygon geometry. 
     248         */ 
     249        polygon: function(node) { 
     250            var nodeList = this.getElementsByTagNameNS(node, this.kmlns, 
     251                                                       "LinearRing"); 
     252            var components = []; 
     253            if(nodeList.length > 0) { 
     254                // this assumes exterior ring first, inner rings after 
     255                var ring; 
     256                for(var i=0; i<nodeList.length; ++i) { 
     257                    ring = this.parseGeometry.linestring.apply(this, 
     258                                                        [nodeList[i], true]); 
     259                    if(ring) { 
     260                        components.push(ring); 
     261                    } 
     262                } 
     263            } 
     264            return new OpenLayers.Geometry.Polygon(components); 
     265        }, 
     266         
     267        /** 
     268         * Method: parseGeometry.multigeometry 
     269         * Given a KML node representing a multigeometry, create an 
     270         *     OpenLayers geometry collection. 
     271         * 
     272         * Parameters: 
     273         * node - {DOMElement} A KML MultiGeometry node. 
     274         * 
     275         * Returns: 
     276         * {<OpenLayers.Geometry.Polygon>} A geometry collection. 
     277         */ 
     278        multigeometry: function(node) { 
     279            var child, parser; 
     280            var parts = []; 
     281            var children = node.childNodes; 
     282            for(var i=0; i<children.length; ++i ) { 
     283                child = children[i]; 
     284                if(child.nodeType == 1) { 
     285                    type = (child.prefix) ? 
     286                            child.nodeName.split(":")[1] : 
     287                            child.nodeName; 
     288                    var parser = this.parseGeometry[type.toLowerCase()]; 
     289                    if(parser) { 
     290                        parts.push(parser.apply(this, [child])); 
     291                    } 
     292                } 
     293            } 
     294            return new OpenLayers.Geometry.Collection(parts); 
     295        } 
     296         
     297    }, 
     298 
     299    /** 
    112300     * Method: parseAttributes 
    113      * recursive function parse the attributes of a KML node. 
    114      * Searches for any child nodes which aren't geometries, 
    115      * and gets their value. 
    116301     * 
    117302     * Parameters: 
    118      * xmlNode - {<DOMElement>}  
    119      */ 
    120     parseAttributes: function(xmlNode) { 
    121         var nodes = xmlNode.childNodes; 
     303     * node - {<DOMElement>} 
     304     * 
     305     * Returns: 
     306     * {Object} An attributes object. 
     307     */ 
     308    parseAttributes: function(node) { 
    122309        var attributes = {}; 
    123         for(var i = 0; i < nodes.length; i++) { 
    124             var name = nodes[i].nodeName; 
    125             var value = OpenLayers.Util.getXmlNodeValue(nodes[i]); 
    126             // Ignore Geometry attributes 
    127             // match ".//gml:pos|.//gml:posList|.//gml:coordinates" 
    128             if((name.search(":pos")!=-1) 
    129               ||(name.search(":posList")!=-1) 
    130               ||(name.search(":coordinates")!=-1)){ 
    131                continue;     
    132             } 
    133              
    134             // Check for a leaf node 
    135             if((nodes[i].childNodes.length == 1 && nodes[i].childNodes[0].nodeName == "#text") 
    136                 || (nodes[i].childNodes.length == 0 && nodes[i].nodeName!="#text")) { 
    137                 attributes[name] = value; 
    138             } 
    139             OpenLayers.Util.extend(attributes, this.parseAttributes(nodes[i])) 
    140         }    
     310        // assume attribute nodes are type 1 children with a type 3 child 
     311        var child, grandchildren, grandchild; 
     312        var children = node.childNodes; 
     313        for(var i=0; i<children.length; ++i) { 
     314            child = children[i]; 
     315            if(child.nodeType == 1) { 
     316                grandchildren = child.childNodes; 
     317                if(grandchildren.length == 1) { 
     318                    grandchild = grandchildren[0]; 
     319                    if(grandchild.nodeType == 3) { 
     320                        name = (child.prefix) ? 
     321                                child.nodeName.split(":")[1] : 
     322                                child.nodeName; 
     323                        value = grandchild.nodeValue.replace( 
     324                                                this.regExes.trimSpace, ""); 
     325                        attributes[name] = value; 
     326                    } 
     327                } 
     328            } 
     329        } 
    141330        return attributes; 
    142331    }, 
    143      
    144     /** 
    145      * Method: parseCoords 
    146      * Extract Geographic coordinates from an XML node. 
     332 
     333    /** 
     334     * APIMethod: write 
     335     * Accept Feature Collection, and return a string.  
     336     *  
     337     * Parameters: 
     338     * features - An array of <OpenLayers.Feature.Vector> features. 
    147339     * 
     340     * Returns: 
     341     * {String} A KML string. 
     342     */ 
     343     write: function(features) { 
     344        if(!(features instanceof Array)) { 
     345            features = [features]; 
     346        } 
     347        var kml = this.createElementNS(this.kmlns, "kml"); 
     348        var folder = this.createFolderXML(); 
     349        for(var i=0; i<features.length; ++i) { 
     350            folder.appendChild(this.createPlacemarkXML(features[i])); 
     351        } 
     352        kml.appendChild(folder); 
     353        return OpenLayers.Format.XML.prototype.write.apply(this, [kml]); 
     354     }, 
     355 
     356    /** 
     357     * Method: createFolderXML 
     358     * Creates and returns a KML folder node 
     359     *  
     360     * Returns: 
     361     * {DOMElement} 
     362     */ 
     363    createFolderXML: function() { 
     364        // Folder name 
     365        var folderName = this.createElementNS(this.kmlns, "name"); 
     366        var folderNameText = this.createTextNode(this.foldersName);  
     367        folderName.appendChild(folderNameText); 
     368 
     369        // Folder description 
     370        var folderDesc = this.createElementNS(this.kmlns, "description");         
     371        var folderDescText = this.createTextNode(this.foldersDesc);  
     372        folderDesc.appendChild(folderDescText); 
     373 
     374        // Folder 
     375        var folder = this.createElementNS(this.kmlns, "Folder"); 
     376        folder.appendChild(folderName); 
     377        folder.appendChild(folderDesc); 
     378         
     379        return folder; 
     380    }, 
     381 
     382    /** 
     383     * Method: createPlacemarkXML 
     384     * Creates and returns a KML placemark node representing the given feature.  
     385     *  
    148386     * Parameters: 
    149      * xmlNode - {<XMLNode>}  
     387     * feature - {<OpenLayers.Feature.Vector>} 
    150388     *  
    151389     * Returns: 
    152      * An array of <OpenLayers.Geometry.Point> points. 
    153      */ 
    154     parseCoords: function(xmlNode) { 
    155         var p = []; 
    156         p.points = []; 
    157         // TBD: Need to handle an array of coordNodes not just coordNodes[0] 
    158          
    159         var coordNodes = this.getElementsByTagNameNS(xmlNode, 
    160                                                      this.kmlns, 
    161                                                      "coordinates")[0]; 
    162         var coordString = OpenLayers.Util.getXmlNodeValue(coordNodes); 
    163          
    164         var firstCoord = coordString.split(" "); 
    165          
    166         while (firstCoord[0] == "")  
    167             firstCoord.shift(); 
    168          
    169         var dim = firstCoord[0].split(",").length; 
    170  
    171         // Extract an array of Numbers from CoordString 
    172         var nums = (coordString) ? coordString.split(/[, \n\t]+/) : []; 
    173          
    174          
    175         // Remove elements caused by leading and trailing white space 
    176         while (nums[0] == "")  
    177             nums.shift(); 
    178          
    179         while (nums[nums.length-1] == "")  
    180             nums.pop(); 
    181          
    182         for(i = 0; i < nums.length; i = i + dim) { 
    183             x = parseFloat(nums[i]); 
    184             y = parseFloat(nums[i+1]); 
    185             p.points.push(new OpenLayers.Geometry.Point(x, y)); 
    186              
    187             if (!p.bounds) { 
    188                 p.bounds = new OpenLayers.Bounds(x, y, x, y); 
    189             } else { 
    190                 p.bounds.extend(x, y); 
    191             } 
    192         } 
    193         return p; 
     390     * {DOMElement} 
     391     */ 
     392    createPlacemarkXML: function(feature) {         
     393        // Placemark name 
     394        var placemarkName = this.createElementNS(this.kmlns, "name"); 
     395        var name = (feature.attributes.name) ? 
     396                    feature.attributes.name : feature.id; 
     397        placemarkName.appendChild(this.createTextNode(name)); 
     398 
     399        // Placemark description 
     400        var placemarkDesc = this.createElementNS(this.kmlns, "description"); 
     401        var desc = (feature.attributes.description) ? 
     402                    feature.attributes.description : this.placemarksDesc; 
     403        placemarkDesc.appendChild(this.createTextNode(desc)); 
     404         
     405        // Placemark 
     406        var placemarkNode = this.createElementNS(this.kmlns, "Placemark"); 
     407        if(feature.fid != null) { 
     408            placemarkNode.setAttribute("id", feature.fid); 
     409        } 
     410        placemarkNode.appendChild(placemarkName); 
     411        placemarkNode.appendChild(placemarkDesc); 
     412 
     413        // Geometry node (Point, LineString, etc. nodes) 
     414        var geometryNode = this.buildGeometryNode(feature.geometry); 
     415        placemarkNode.appendChild(geometryNode);         
     416         
     417        // TBD - deal with remaining (non name/description) attributes. 
     418        return placemarkNode; 
     419    },     
     420 
     421    /** 
     422     * Method: buildGeometryNode 
     423     * Builds and returns a KML geometry node with the given geometry. 
     424     *  
     425     * Parameters: 
     426     * geometry - {<OpenLayers.Geometry>} 
     427     *  
     428     * Returns: 
     429     * {DOMElement} 
     430     */ 
     431    buildGeometryNode: function(geometry) { 
     432        var className = geometry.CLASS_NAME; 
     433        var type = className.substring(className.lastIndexOf(".") + 1); 
     434        var builder = this.buildGeometry[type.toLowerCase()]; 
     435        var node = null; 
     436        if(builder) { 
     437            node = builder.apply(this, [geometry]); 
     438        } 
     439        return node; 
    194440    }, 
    195441 
     442    /** 
     443     * Property: buildGeometry 
     444     * Object containing methods to do the actual geometry node building 
     445     *     based on geometry type. 
     446     */ 
     447    buildGeometry: { 
     448        // TBD: Anybody care about namespace aliases here (these nodes have 
     449        //    no prefixes)? 
     450 
     451        /** 
     452         * Method: buildGeometry.point 
     453         * Given an OpenLayers point geometry, create a KML point. 
     454         * 
     455         * Parameters: 
     456         * geometry - {<OpenLayers.Geometry.Point>} A point geometry. 
     457         * 
     458         * Returns: 
     459         * {DOMElement} A KML point node. 
     460         */ 
     461        point: function(geometry) { 
     462            var kml = this.createElementNS(this.kmlns, "Point"); 
     463            kml.appendChild(this.buildCoordinatesNode(geometry)); 
     464            return kml; 
     465        }, 
     466         
     467        /** 
     468         * Method: buildGeometry.multipoint 
     469         * Given an OpenLayers multipoint geometry, create a KML 
     470         *     GeometryCollection. 
     471         * 
     472         * Parameters: 
     473         * geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry. 
     474         * 
     475         * Returns: 
     476         * {DOMElement} A KML GeometryCollection node. 
     477         */ 
     478        multipoint: function(geometry) { 
     479            return this.buildGeometry.collection(geometry); 
     480        }, 
     481 
     482        /** 
     483         * Method: buildGeometry.linestring 
     484         * Given an OpenLayers linestring geometry, create a KML linestring. 
     485         * 
     486         * Parameters: 
     487         * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry. 
     488         * 
     489         * Returns: 
     490         * {DOMElement} A KML linestring node. 
     491         */ 
     492        linestring: function(geometry) { 
     493            var kml = this.createElementNS(this.kmlns, "LineString"); 
     494            kml.appendChild(this.buildCoordinatesNode(geometry)); 
     495            return kml; 
     496        }, 
     497         
     498        /** 
     499         * Method: buildGeometry.multilinestring 
     500         * Given an OpenLayers multilinestring geometry, create a KML 
     501         *     GeometryCollection. 
     502         * 
     503         * Parameters: 
     504         * geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry. 
     505         * 
     506         * Returns: 
     507         * {DOMElement} A KML GeometryCollection node. 
     508         */ 
     509        multilinestring: function(geometry) { 
     510            return this.buildGeometry.collection(geometry); 
     511        }, 
     512 
     513        /** 
     514         * Method: buildGeometry.linearring 
     515         * Given an OpenLayers linearring geometry, create a KML linearring. 
     516         * 
     517         * Parameters: 
     518         * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry. 
     519         * 
     520         * Returns: 
     521         * {DOMElement} A KML linearring node. 
     522         */ 
     523        linearring: function(geometry) { 
     524            var kml = this.createElementNS(this.kmlns, "LinearRing"); 
     525            kml.appendChild(this.buildCoordinatesNode(geometry)); 
     526            return kml; 
     527        }, 
     528         
     529        /** 
     530         * Method: buildGeometry.polygon 
     531         * Given an OpenLayers polygon geometry, create a KML polygon. 
     532         * 
     533         * Parameters: 
     534         * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry. 
     535         * 
     536         * Returns: 
     537         * {DOMElement} A KML polygon node. 
     538         */ 
     539        polygon: function(geometry) { 
     540            var kml = this.createElementNS(this.kmlns, "Polygon"); 
     541            var rings = geometry.components; 
     542            var ringMember, ringGeom, type; 
     543            for(var i=0; i<rings.length; ++i) { 
     544                type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs"; 
     545                ringMember = this.createElementNS(this.kmlns, type); 
     546                ringGeom = this.buildGeometry.linearring.apply(this, 
     547                                                               [rings[i]]); 
     548                ringMember.appendChild(ringGeom); 
     549                kml.appendChild(ringMember); 
     550            } 
     551            return kml; 
     552        }, 
     553         
     554        /** 
     555         * Method: buildGeometry.multipolygon 
     556         * Given an OpenLayers multipolygon geometry, create a KML 
     557         *     GeometryCollection. 
     558         * 
     559         * Parameters: 
     560         * geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry. 
     561         * 
     562         * Returns: 
     563         * {DOMElement} A KML GeometryCollection node. 
     564         */ 
     565        multipolygon: function(geometry) { 
     566            return this.buildGeometry.collection(geometry); 
     567        }, 
     568 
     569        /** 
     570         * Method: buildGeometry.collection 
     571         * Given an OpenLayers geometry collection, create a KML MultiGeometry. 
     572         * 
     573         * Parameters: 
     574         * geometry - {<OpenLayers.Geometry.Collection>} A geometry collection. 
     575         * 
     576         * Returns: 
     577         * {DOMElement} A KML MultiGeometry node. 
     578         */ 
     579        collection: function(geometry) { 
     580            var kml = this.createElementNS(this.kmlns, "MultiGeometry"); 
     581            var child; 
     582            for(var i=0; i<geometry.components.length; ++i) { 
     583                child = this.buildGeometryNode.apply(this, 
     584                                                     [geometry.components[i]]); 
     585                if(child) { 
     586                    kml.appendChild(child); 
     587                } 
     588            } 
     589            return kml; 
     590        } 
     591    }, 
     592 
     593    /** 
     594     * Method: buildCoordinatesNode 
     595     * Builds and returns the KML coordinates node with the given geometry 
     596     * <coordinates>...</coordinates> 
     597     *  
     598     * Parameters: 
     599     * geometry - {<OpenLayers.Geometry>} 
     600     *  
     601     * Return: 
     602     * {DOMElement} 
     603     */      
     604    buildCoordinatesNode: function(geometry) { 
     605        var coordinatesNode = this.createElementNS(this.kmlns, "coordinates"); 
     606         
     607        var path; 
     608        var points = geometry.components; 
     609        if(points) { 
     610            // LineString or LinearRing 
     611            var point; 
     612            var numPoints = points.length; 
     613            var parts = new Array(numPoints); 
     614            for(var i=0; i<numPoints; ++i) { 
     615                point = points[i]; 
     616                parts[i] = point.x + "," + point.y; 
     617            } 
     618            path = parts.join(" "); 
     619        } else { 
     620            // Point 
     621            path = geometry.x + "," + geometry.y; 
     622        } 
     623        var points = null; 
     624        if(geometry.components) { 
     625            points = geometry.components; 
     626        } 
     627         
     628        var txtNode = this.createTextNode(path); 
     629        coordinatesNode.appendChild(txtNode); 
     630         
     631        return coordinatesNode; 
     632    },     
     633 
    196634    CLASS_NAME: "OpenLayers.Format.KML"  
    197 });      
     635});