OpenLayers OpenLayers

Changeset 4219

Show
Ignore:
Timestamp:
09/11/07 11:18:42 (1 year ago)
Author:
tschaub
Message:

Full read/write support for KML. All KML 2.1 geometries supported. All OL geometries supported (closes #927).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/openlayers/examples/vector-formats.html

    r4209 r4219  
    7878                wkt: new OpenLayers.Format.WKT(), 
    7979                geojson: new OpenLayers.Format.GeoJSON(), 
    80                 gml: new OpenLayers.Format.GML() //
    81                 //kml: new OpenLayers.Format.KML() 
     80                gml: new OpenLayers.Format.GML()
     81                kml: new OpenLayers.Format.KML() 
    8282            }; 
    8383             
     
    147147            <select name="formatType" id="formatType"> 
    148148                <option value="geojson" selected="selected">GeoJSON</option> 
    149                 <!--<option value="kml">KML</option>--
     149                <option value="kml">KML</option
    150150                <option value="gml">GML</option> 
    151151                <option value="wkt">Well-Known Text (WKT)</option> 
  • trunk/openlayers/lib/OpenLayers/Format/KML.js

    r4110 r4219  
    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") {  
    47             data = OpenLayers.parseXMLString(data); 
    48         }     
    49         var featureNodes = OpenLayers.Ajax.getElementsByTagNameNS(data, this.kmlns, "", "Placemark"); 
    50          
    51         var features = []; 
    52          
    53         // Process all the featureMembers 
    54         for (var i = 0; i < featureNodes.length; i++) { 
     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") { 
     79            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); 
     80        } 
     81        var featureNodes = this.getElementsByTagNameNS(data, 
     82                                                       this.kmlns, 
     83                                                       "Placemark"); 
     84        var numFeatures = featureNodes.length; 
     85        var features = new Array(numFeatures); 
     86        for(var i=0; i<numFeatures; i++) { 
    5587            var feature = this.parseFeature(featureNodes[i]); 
    56  
    57             if (feature) { 
    58                 features.push(feature); 
     88            if(feature) { 
     89                features[i] = feature; 
     90            } else { 
     91                throw "Bad Placemark: " + i; 
    5992            } 
    6093        } 
    6194        return features; 
    62      }, 
    63  
    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) { 
    74         var geom; 
    75         var p; // [points,bounds] 
    76  
    77         var feature = new OpenLayers.Feature.Vector(); 
    78  
    79         // match Point 
    80         if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, 
    81             this.kmlns, "", "Point").length != 0) { 
    82             var point = OpenLayers.Ajax.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 (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, 
    94             this.kmlns, "", "LineString").length != 0) { 
    95             var linestring = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, 
    96                 this.kmlns, "", "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          
     95    }, 
     96 
     97    /** 
     98     * Method: parseFeature 
     99     * This function is the core of the KML parsing code in OpenLayers. 
     100     *     It creates the geometries that are then attached to the returned 
     101     *     feature, and calls parseAttributes() to get attribute data out. 
     102     * 
     103     * Parameters: 
     104     * node - {<DOMElement>} 
     105     * 
     106     * Returns: 
     107     * {<OpenLayers.Feature.Vector>} A vector feature. 
     108     */ 
     109    parseFeature: function(node) { 
     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 > 1) { 
     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            } else { 
     182                throw "Bad coordinate string: " + coordString; 
     183            } 
     184            return point; 
     185        }, 
     186         
     187        /** 
     188         * Method: parseGeometry.linestring 
     189         * Given a KML node representing a linestring geometry, create an 
     190         *     OpenLayers linestring geometry. 
     191         * 
     192         * Parameters: 
     193         * node - {DOMElement} A KML LineString node. 
     194         * 
     195         * Returns: 
     196         * {<OpenLayers.Geometry.LineString>} A linestring geometry. 
     197         */ 
     198        linestring: function(node, ring) { 
     199            var nodeList = this.getElementsByTagNameNS(node, this.kmlns, 
     200                                                       "coordinates"); 
     201            var line = null; 
     202            if(nodeList.length > 0) { 
     203                var coordString = nodeList[0].firstChild.nodeValue; 
     204                coordString = coordString.replace(this.regExes.trimSpace, 
     205                                                  ""); 
     206                coordString = coordString.replace(this.regExes.trimComma, 
     207                                                  ","); 
     208                var pointList = coordString.split(this.regExes.splitSpace); 
     209                var numPoints = pointList.length; 
     210                var points = new Array(numPoints); 
     211                var coords, numCoords; 
     212                for(var i=0; i<numPoints; ++i) { 
     213                    coords = pointList[i].split(","); 
     214                    numCoords = coords.length; 
     215                    if(numCoords > 1) { 
     216                        if(coords.length == 2) { 
     217                            coords[2] = null; 
     218                        } 
     219                        points[i] = new OpenLayers.Geometry.Point(coords[0], 
     220                                                                  coords[1], 
     221                                                                  coords[2]); 
     222                    } else { 
     223                        throw "Bad LineString point coordinates: " + 
     224                              pointList[i]; 
     225                    } 
     226                } 
     227                if(numPoints) { 
     228                    if(ring) { 
     229                        line = new OpenLayers.Geometry.LinearRing(points); 
     230                    } else { 
     231                        line = new OpenLayers.Geometry.LineString(points); 
     232                    } 
     233                } else { 
     234                    throw "Bad LineString coordinates: " + coordString; 
     235                } 
     236            } 
     237 
     238            return line; 
     239        }, 
     240         
     241        /** 
     242         * Method: parseGeometry.polygon 
     243         * Given a KML node representing a polygon geometry, create an 
     244         *     OpenLayers polygon geometry. 
     245         * 
     246         * Parameters: 
     247         * node - {DOMElement} A KML Polygon node. 
     248         * 
     249         * Returns: 
     250         * {<OpenLayers.Geometry.Polygon>} A polygon geometry. 
     251         */ 
     252        polygon: function(node) { 
     253            var nodeList = this.getElementsByTagNameNS(node, this.kmlns, 
     254                                                       "LinearRing"); 
     255            var numRings = nodeList.length; 
     256            var components = new Array(numRings); 
     257            if(numRings > 0) { 
     258                // this assumes exterior ring first, inner rings after 
     259                var ring; 
     260                for(var i=0; i<nodeList.length; ++i) { 
     261                    ring = this.parseGeometry.linestring.apply(this, 
     262                                                        [nodeList[i], true]); 
     263                    if(ring) { 
     264                        components[i] = ring; 
     265                    } else { 
     266                        throw "Bad LinearRing geometry: " + i; 
     267                    } 
     268                } 
     269            } 
     270            return new OpenLayers.Geometry.Polygon(components); 
     271        }, 
     272         
     273        /** 
     274         * Method: parseGeometry.multigeometry 
     275         * Given a KML node representing a multigeometry, create an 
     276         *     OpenLayers geometry collection. 
     277         * 
     278         * Parameters: 
     279         * node - {DOMElement} A KML MultiGeometry node. 
     280         * 
     281         * Returns: 
     282         * {<OpenLayers.Geometry.Polygon>} A geometry collection. 
     283         */ 
     284        multigeometry: function(node) { 
     285            var child, parser; 
     286            var parts = []; 
     287            var children = node.childNodes; 
     288            for(var i=0; i<children.length; ++i ) { 
     289                child = children[i]; 
     290                if(child.nodeType == 1) { 
     291                    type = (child.prefix) ? 
     292                            child.nodeName.split(":")[1] : 
     293                            child.nodeName; 
     294                    var parser = this.parseGeometry[type.toLowerCase()]; 
     295                    if(parser) { 
     296                        parts.push(parser.apply(this, [child])); 
     297                    } 
     298                } 
     299            } 
     300            return new OpenLayers.Geometry.Collection(parts); 
     301        } 
     302         
     303    }, 
     304 
     305    /** 
    112306     * 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. 
    116307     * 
    117308     * Parameters: 
    118      * xmlNode - {<DOMElement>}  
    119      */ 
    120     parseAttributes: function(xmlNode) { 
    121         var nodes = xmlNode.childNodes; 
     309     * node - {<DOMElement>} 
     310     * 
     311     * Returns: 
     312     * {Object} An attributes object. 
     313     */ 
     314    parseAttributes: function(node) { 
    122315        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         }    
     316        // assume attribute nodes are type 1 children with a type 3 child 
     317        var child, grandchildren, grandchild; 
     318        var children = node.childNodes; 
     319        for(var i=0; i<children.length; ++i) { 
     320            child = children[i]; 
     321            if(child.nodeType == 1) { 
     322                grandchildren = child.childNodes; 
     323                if(grandchildren.length == 1) { 
     324                    grandchild = grandchildren[0]; 
     325                    if(grandchild.nodeType == 3) { 
     326                        name = (child.prefix) ? 
     327                                child.nodeName.split(":")[1] : 
     328                                child.nodeName; 
     329                        value = grandchild.nodeValue.replace( 
     330                                                this.regExes.trimSpace, ""); 
     331                        attributes[name] = value; 
     332                    } 
     333                } 
     334            } 
     335        } 
    141336        return attributes; 
    142337    }, 
    143      
    144     /** 
    145      * Method: parseCoords 
    146      * Extract Geographic coordinates from an XML node. 
     338 
     339    /** 
     340     * APIMethod: write 
     341     * Accept Feature Collection, and return a string.  
     342     *  
     343     * Parameters: 
     344     * features - An array of <OpenLayers.Feature.Vector> features. 
    147345     * 
     346     * Returns: 
     347     * {String} A KML string. 
     348     */ 
     349     write: function(features) { 
     350        if(!(features instanceof Array)) { 
     351            features = [features]; 
     352        } 
     353        var kml = this.createElementNS(this.kmlns, "kml"); 
     354        var folder = this.createFolderXML(); 
     355        for(var i=0; i<features.length; ++i) { 
     356            folder.appendChild(this.createPlacemarkXML(features[i])); 
     357        } 
     358        kml.appendChild(folder); 
     359        return OpenLayers.Format.XML.prototype.write.apply(this, [kml]); 
     360     }, 
     361 
     362    /** 
     363     * Method: createFolderXML 
     364     * Creates and returns a KML folder node 
     365     *  
     366     * Returns: 
     367     * {DOMElement} 
     368     */ 
     369    createFolderXML: function() { 
     370        // Folder name 
     371        var folderName = this.createElementNS(this.kmlns, "name"); 
     372        var folderNameText = this.createTextNode(this.foldersName);  
     373        folderName.appendChild(folderNameText); 
     374 
     375        // Folder description 
     376        var folderDesc = this.createElementNS(this.kmlns, "description");         
     377        var folderDescText = this.createTextNode(this.foldersDesc);  
     378        folderDesc.appendChild(folderDescText); 
     379 
     380        // Folder 
     381        var folder = this.createElementNS(this.kmlns, "Folder"); 
     382        folder.appendChild(folderName); 
     383        folder.appendChild(folderDesc); 
     384         
     385        return folder; 
     386    }, 
     387 
     388    /** 
     389     * Method: createPlacemarkXML 
     390     * Creates and returns a KML placemark node representing the given feature.  
     391     *  
    148392     * Parameters: 
    149      * xmlNode - {<XMLNode>}  
     393     * feature - {<OpenLayers.Feature.Vector>} 
    150394     *  
    151395     * 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 = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.kmlns, "", "coordinates")[0]; 
    160         var coordString = OpenLayers.Util.getXmlNodeValue(coordNodes); 
    161          
    162         var firstCoord = coordString.split(" "); 
    163          
    164         while (firstCoord[0] == "")  
    165             firstCoord.shift(); 
    166          
    167         var dim = firstCoord[0].split(",").length; 
    168  
    169         // Extract an array of Numbers from CoordString 
    170         var nums = (coordString) ? coordString.split(/[, \n\t]+/) : []; 
    171          
    172          
    173         // Remove elements caused by leading and trailing white space 
    174         while (nums[0] == "")  
    175             nums.shift(); 
    176          
    177         while (nums[nums.length-1] == "")  
    178             nums.pop(); 
    179          
    180         for(i = 0; i < nums.length; i = i + dim) { 
    181             x = parseFloat(nums[i]); 
    182             y = parseFloat(nums[i+1]); 
    183             p.points.push(new OpenLayers.Geometry.Point(x, y)); 
    184              
    185             if (!p.bounds) { 
    186                 p.bounds = new OpenLayers.Bounds(x, y, x, y); 
    187             } else { 
    188                 p.bounds.extend(x, y); 
    189             } 
    190         } 
    191         return p; 
     396     * {DOMElement} 
     397     */ 
     398    createPlacemarkXML: function(feature) {         
     399        // Placemark name 
     400        var placemarkName = this.createElementNS(this.kmlns, "name"); 
     401        var name = (feature.attributes.name) ? 
     402                    feature.attributes.name : feature.id; 
     403        placemarkName.appendChild(this.createTextNode(name)); 
     404 
     405        // Placemark description 
     406        var placemarkDesc = this.createElementNS(this.kmlns, "description"); 
     407        var desc = (feature.attributes.description) ? 
     408                    feature.attributes.description : this.placemarksDesc; 
     409        placemarkDesc.appendChild(this.createTextNode(desc)); 
     410         
     411        // Placemark 
     412        var placemarkNode = this.createElementNS(this.kmlns, "Placemark"); 
     413        if(feature.fid != null) { 
     414            placemarkNode.setAttribute("id", feature.fid); 
     415        } 
     416        placemarkNode.appendChild(placemarkName); 
     417        placemarkNode.appendChild(placemarkDesc); 
     418 
     419        // Geometry node (Point, LineString, etc. nodes) 
     420        var geometryNode = this.buildGeometryNode(feature.geometry); 
     421        placemarkNode.appendChild(geometryNode);         
     422         
     423        // TBD - deal with remaining (non name/description) attributes. 
     424        return placemarkNode; 
     425    },     
     426 
     427    /** 
     428     * Method: buildGeometryNode 
     429     * Builds and returns a KML geometry node with the given geometry. 
     430     *  
     431     * Parameters: 
     432     * geometry - {<OpenLayers.Geometry>} 
     433     *  
     434     * Returns: 
     435     * {DOMElement} 
     436     */ 
     437    buildGeometryNode: function(geometry) { 
     438        var className = geometry.CLASS_NAME; 
     439        var type = className.substring(className.lastIndexOf(".") + 1); 
     440        var builder = this.buildGeometry[type.toLowerCase()]; 
     441        var node = null; 
     442        if(builder) { 
     443            node = builder.apply(this, [geometry]); 
     444        } 
     445        return node; 
    192446    }, 
    193447 
     448    /** 
     449     * Property: buildGeometry 
     450     * Object containing methods to do the actual geometry node building 
     451     *     based on geometry type. 
     452     */ 
     453    buildGeometry: { 
     454        // TBD: Anybody care about namespace aliases here (these nodes have 
     455        //    no prefixes)? 
     456 
     457        /** 
     458         * Method: buildGeometry.point 
     459         * Given an OpenLayers point geometry, create a KML point. 
     460         * 
     461         * Parameters: 
     462         * geometry - {<OpenLayers.Geometry.Point>} A point geometry. 
     463         * 
     464         * Returns: 
     465         * {DOMElement} A KML point node. 
     466         */ 
     467        point: function(geometry) { 
     468            var kml = this.createElementNS(this.kmlns, "Point"); 
     469            kml.appendChild(this.buildCoordinatesNode(geometry)); 
     470            return kml; 
     471        }, 
     472         
     473        /** 
     474         * Method: buildGeometry.multipoint 
     475         * Given an OpenLayers multipoint geometry, create a KML 
     476         *     GeometryCollection. 
     477         * 
     478         * Parameters: 
     479         * geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry. 
     480         * 
     481         * Returns: 
     482         * {DOMElement} A KML GeometryCollection node. 
     483         */ 
     484        multipoint: function(geometry) { 
     485            return this.buildGeometry.collection(geometry); 
     486        }, 
     487 
     488        /** 
     489         * Method: buildGeometry.linestring 
     490         * Given an OpenLayers linestring geometry, create a KML linestring. 
     491         * 
     492         * Parameters: 
     493         * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry. 
     494         * 
     495         * Returns: 
     496         * {DOMElement} A KML linestring node. 
     497         */ 
     498        linestring: function(geometry) { 
     499            var kml = this.createElementNS(this.kmlns, "LineString"); 
     500            kml.appendChild(this.buildCoordinatesNode(geometry)); 
     501            return kml; 
     502        }, 
     503         
     504        /** 
     505         * Method: buildGeometry.multilinestring 
     506         * Given an OpenLayers multilinestring geometry, create a KML 
     507         *     GeometryCollection. 
     508         * 
     509         * Parameters: 
     510         * geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry. 
     511         * 
     512         * Returns: 
     513         * {DOMElement} A KML GeometryCollection node. 
     514         */ 
     515        multilinestring: function(geometry) { 
     516            return this.buildGeometry.collection(geometry); 
     517        }, 
     518 
     519        /** 
     520         * Method: buildGeometry.linearring 
     521         * Given an OpenLayers linearring geometry, create a KML linearring. 
     522         * 
     523         * Parameters: 
     524         * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry. 
     525         * 
     526         * Returns: 
     527         * {DOMElement} A KML linearring node. 
     528         */ 
     529        linearring: function(geometry) { 
     530            var kml = this.createElementNS(this.kmlns, "LinearRing"); 
     531            kml.appendChild(this.buildCoordinatesNode(geometry)); 
     532            return kml; 
     533        }, 
     534         
     535        /** 
     536         * Method: buildGeometry.polygon 
     537         * Given an OpenLayers polygon geometry, create a KML polygon. 
     538         * 
     539         * Parameters: 
     540         * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry. 
     541         * 
     542         * Returns: 
     543         * {DOMElement} A KML polygon node. 
     544         */ 
     545        polygon: function(geometry) { 
     546            var kml = this.createElementNS(this.kmlns, "Polygon"); 
     547            var rings = geometry.components; 
     548            var ringMember, ringGeom, type; 
     549            for(var i=0; i<rings.length; ++i) { 
     550                type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs"; 
     551                ringMember = this.createElementNS(this.kmlns, type); 
     552                ringGeom = this.buildGeometry.linearring.apply(this, 
     553                                                               [rings[i]]); 
     554                ringMember.appendChild(ringGeom); 
     555                kml.appendChild(ringMember); 
     556            } 
     557            return kml; 
     558        }, 
     559         
     560        /** 
     561         * Method: buildGeometry.multipolygon 
     562         * Given an OpenLayers multipolygon geometry, create a KML 
     563         *     GeometryCollection. 
     564         * 
     565         * Parameters: 
     566         * geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry. 
     567         * 
     568         * Returns: 
     569         * {DOMElement} A KML GeometryCollection node. 
     570         */ 
     571        multipolygon: function(geometry) { 
     572            return this.buildGeometry.collection(geometry); 
     573        }, 
     574 
     575        /** 
     576         * Method: buildGeometry.collection 
     577         * Given an OpenLayers geometry collection, create a KML MultiGeometry. 
     578         * 
     579         * Parameters: 
     580         * geometry - {<OpenLayers.Geometry.Collection>} A geometry collection. 
     581         * 
     582         * Returns: 
     583         * {DOMElement} A KML MultiGeometry node. 
     584         */ 
     585        collection: function(geometry) { 
     586            var kml = this.createElementNS(this.kmlns, "MultiGeometry"); 
     587            var child; 
     588            for(var i=0; i<geometry.components.length; ++i) { 
     589                child = this.buildGeometryNode.apply(this, 
     590                                                     [geometry.components[i]]); 
     591                if(child) { 
     592                    kml.appendChild(child); 
     593                } 
     594            } 
     595            return kml; 
     596        } 
     597    }, 
     598 
     599    /** 
     600     * Method: buildCoordinatesNode 
     601     * Builds and returns the KML coordinates node with the given geometry 
     602     * <coordinates>...</coordinates> 
     603     *  
     604     * Parameters: 
     605     * geometry - {<OpenLayers.Geometry>} 
     606     *  
     607     * Return: 
     608     * {DOMElement} 
     609     */      
     610    buildCoordinatesNode: function(geometry) { 
     611        var coordinatesNode = this.createElementNS(this.kmlns, "coordinates"); 
     612         
     613        var path; 
     614        var points = geometry.components; 
     615        if(points) { 
     616            // LineString or LinearRing 
     617            var point; 
     618            var numPoints = points.length; 
     619            var parts = new Array(numPoints); 
     620            for(var i=0; i<numPoints; ++i) { 
     621                point = points[i]; 
     622                parts[i] = point.x + "," + point.y; 
     623            } 
     624            path = parts.join(" "); 
     625        } else { 
     626            // Point 
     627            path = geometry.x + "," + geometry.y; 
     628        } 
     629         
     630        var txtNode = this.createTextNode(path); 
     631        coordinatesNode.appendChild(txtNode); 
     632         
     633        return coordinatesNode; 
     634    },     
     635 
    194636    CLASS_NAME: "OpenLayers.Format.KML"  
    195 });      
     637}); 
  • trunk/openlayers/tests/Format/test_KML.html

    r4188 r4219  
    44    <script type="text/javascript"> 
    55 
     6    var test_content = '<kml xmlns="http://earth.google.com/kml/2.0"><Folder><name>OpenLayers export</name><description>Vector geometries from OpenLayers</description><Placemark id="KML.Polygon"><name>OpenLayers.Feature.Vector_344</name><description>A KLM Polygon</description><Polygon><outerBoundaryIs><LinearRing><coordinates>5.001370157823406,49.26855713824488 8.214706453896161,49.630662409673505 8.397385910100951,48.45172350357396 5.001370157823406,49.26855713824488</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark><Placemark id="KML.LineString"><name>OpenLayers.Feature.Vector_402</name><description>A KML LineString</description><LineString><coordinates>5.838523393080493,49.74814616928052 5.787079558782349,48.410795432216574 8.91427702008381,49.28932499608202</coordinates></LineString></Placemark><Placemark id="KML.Point"><name>OpenLayers.Feature.Vector_451</name><description>A KML Point</description><Point><coordinates>6.985073041685488,49.8682250149058</coordinates></Point></Placemark><Placemark id="KML.MultiGeometry"><name>SF Marina Harbor Master</name><description>KML MultiGeometry</description><MultiGeometry><LineString><coordinates>-122.4425587930444,37.80666418607323 -122.4428379594768,37.80663578323093</coordinates></LineString><LineString><coordinates>-122.4425509770566,37.80662588061205 -122.4428340530617,37.8065999493009</coordinates></LineString></MultiGeometry></Placemark></Folder></kml>'; 
    67 
    78    function test_Format_KML_constructor(t) {  
     
    1516        t.eq(typeof format.read, "function", "format has a read function");  
    1617        t.eq(typeof format.write, "function", "format has a write function"); 
    17  
    1818    } 
    1919 
    2020    function test_Format_KML_read(t) { 
    21         t.plan(1); 
    22         t.ok(true, "Read tests not done yet."); 
     21        t.plan(5); 
     22        var features = (new OpenLayers.Format.KML()).read(this.test_content); 
     23        t.eq(features.length, 4, "Number of features read is correct"); 
     24        t.ok(features[0].geometry.toString() == "POLYGON((5.001370157823406 49.26855713824488,8.214706453896161 49.630662409673505,8.397385910100951 48.45172350357396,5.001370157823406 49.26855713824488))", "polygon feature geometry correctly created"); 
     25        t.ok(features[1].geometry.toString() == "LINESTRING(5.838523393080493 49.74814616928052,5.787079558782349 48.410795432216574,8.91427702008381 49.28932499608202)", "linestring feature geometry correctly created"); 
     26        t.ok(features[2].geometry.toString() == "POINT(6.985073041685488 49.8682250149058)", "point feature geometry correctly created"); 
     27        t.ok(features[3].geometry.CLASS_NAME == "OpenLayers.Geometry.Collection", 
     28             "read geometry collection"); 
    2329    } 
    2430 
    2531    function test_Format_KML_write(t) { 
     32        // make sure id, name, and description are preserved 
    2633        t.plan(1); 
    27         t.ok(true, "Write tests not done yet."); 
     34        var kmlExpected = this.test_content; 
     35        var options = { 
     36            folderName: "OpenLayers export", 
     37            foldersDesc: "Vector geometries from OpenLayers" 
     38        } 
    2839 
    29         var format = new OpenLayers.Format.KML(); 
    30         //var doc = format.read(text); 
    31         //var out = format.write(doc); 
    32         //out = out.replace(/[\r\n]/g, ''); 
    33         //t.eq(text, out, 
    34         //     "correctly writes an KML DOM doc"); 
     40        var format = new OpenLayers.Format.KML(options); 
     41        var features = format.read(kmlExpected); 
     42        var kmlOut = format.write(features); 
     43        t.eq(kmlOut, kmlExpected, "correctly writes an KML doc string"); 
    3544    } 
    3645