[OpenLayers-Dev] OpenLayers.Format.KML.write()

Tim Schaub tschaub at openplans.org
Tue Sep 11 13:11:30 EDT 2007


Damien Corpataux wrote:
> Hi Tim,
> 
> I just posted a patch on ticket #927. The patch suits the requirements:
> - KML extends XML
> - XML class methods are used for XML manipulation
> - tested on FF2/Linux and IE7
> - KML.write() returns a string (using XML.write())
> - GGE import of OL exported KML is working, and vice-versa
> 
> My 2 cents
> 
> Cheers,
> Damien
> 

Thanks for the patch Damien.  I rewrote things a bit and put this in 
with r4219 [1].  I added support for KML MultiGeometry and improved the 
read/write stuff so all OL geometries are supported.  Also, 
name/description attributes and feature id survive a round trip through 
read/write.

There are likely still issues with the parser.  In particular, we don't 
write out feature attributes (beyond name/description).

You can play with the vector formats example to see OL KML output [2]. 
Paste in any KML you can find and report back any trouble with the parser.

Tim

[1] http://trac.openlayers.org/changeset/4219

[2] http://openlayers.org/dev/examples/vector-formats.html



> 
> Tim Schaub wrote:
>> I've opened ticket #927 to track this.
>>
>> If anybody has a patch for KML, please attach it to that ticket. 
>> Damien, I've referenced your email on the ticket.
>>
>> Since XML flavors should now all inherit from the XML format class, I'd 
>> say that write will always return a string.
>>
>> Thanks for any contributions.
>> Tim
>>
>>
>> Damien Corpataux wrote:
>>   
>>> Hello,
>>>
>>> As far as I can see, the advantages of doing so are obvious. Althought I 
>>> didn't have time to apply these, here is a new version of the patch:
>>> - puts placemarks in a folder tag (for google earth import)
>>> - bugfix with geometry tags
>>>
>>> Is the question about write() return type (string or DOMDocuemt/DOMNode) 
>>> still open?
>>>
>>> Cheers,
>>> Damien
>>>
>>>
>>> Patch:
>>> Index: /home/dcorpataux/workspace/openlayers/lib/OpenLayers/Format/KML.js
>>> ===================================================================
>>> --- 
>>> /home/dcorpataux/workspace/openlayers/lib/OpenLayers/Format/KML.js    
>>> (revision 3900)
>>> +++ 
>>> /home/dcorpataux/workspace/openlayers/lib/OpenLayers/Format/KML.js    
>>> (working copy)
>>> @@ -8,8 +8,8 @@
>>>   * @requires OpenLayers/Ajax.js
>>>   *
>>>   * Class: OpenLayers.Format.KML
>>> - * Read only KML. Largely Proof of Concept: does not support advanced 
>>> Features,
>>> - *     including Polygons.  Create a new instance with the
>>> + * Read/write KML 2.0. Does not support advanced Features.
>>> + *     Create a new instance with the
>>>   *     <OpenLayers.Format.KML> constructor.
>>>   *
>>>   * Inherits from:
>>> @@ -24,6 +24,24 @@
>>>      kmlns: "http://earth.google.com/kml/2.0",
>>>     
>>>      /**
>>> +     * APIProperty: placemarksDesc
>>> +     * Default description for a placemark
>>> +     */
>>> +    placemarksDesc: "No description available",
>>> +
>>> +    /**
>>> +     * APIProperty: foldersName
>>> +     * Default name for a folder
>>> +     */
>>> +    foldersName: "OpenLayers export",
>>> +
>>> +    /**
>>> +     * APIProperty: foldersDesc
>>> +     * Default description for a folder
>>> +     */
>>> +    foldersDesc: "Exported on " + new Date(),
>>> +
>>> +    /**
>>>       * Constructor: OpenLayers.Format.KML
>>>       * Create a new parser for KML
>>>       *
>>> @@ -46,7 +64,7 @@
>>>          if (typeof data == "string") {
>>>              data = OpenLayers.parseXMLString(data);
>>>          }   
>>> -        var featureNodes = OpenLayers.Ajax.getElementsByTagNameNS(data, 
>>> this.kmlns, "", "Placemark");
>>> +        var featureNodes = OpenLayers.Ajax.getElementsByTagNameNS(data, 
>>> this.kmlns, "kml", "Placemark");
>>>         
>>>          var features = [];
>>>         
>>> @@ -100,6 +118,19 @@
>>>                  // TBD Bounds only set for one of multiple geometries
>>>                  geom.extendBounds(p.bounds);
>>>              }
>>> +
>>> +        // match Polygon
>>> +        } else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
>>> +            this.kmlns, "", "Polygon").length != 0) {
>>> +            var polygon = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
>>> +                this.kmlns, "", "Polygon")[0];
>>> +            p = this.parseCoords(polygon);
>>> +            if (p.points) {
>>> +                var linearRing = new 
>>> OpenLayers.Geometry.LinearRing(p.points);
>>> +                geom = new OpenLayers.Geometry.Polygon(linearRing);
>>> +                // TBD Bounds only set for one of multiple geometries
>>> +                geom.extendBounds(p.bounds);
>>> +            }
>>>          }
>>>         
>>>          feature.geometry = geom;
>>> @@ -191,5 +222,171 @@
>>>          return p;
>>>      },
>>>  
>>> +    /**
>>> +     * Method: write
>>> +     * Accept Feature Collection, and return an XML Document.
>>> +     *
>>> +     * Parameters:
>>> +     * features - An array of <OpenLayers.Feature.Vector> features.
>>> +     *
>>> +     * Returns:
>>> +     * <DOMDocument>
>>> +     */
>>> +     write: function(features) {
>>> +        var kml = document.createElementNS(this.kmlns, "kml");
>>> +        var folder = this.createFolderXML();
>>> +        for (var i=0; i < features.length; i++) {
>>> +            folder.appendChild(this.createPlacemarkXML(features[i]));
>>> +        }
>>> +        kml.appendChild(folder);
>>> +        return kml;
>>> +     },
>>> +
>>> +    /**
>>> +     * Method: createFolderXML
>>> +     * Creates and returns a KML Folder node with default name and 
>>> description
>>> +     *
>>> +     * Returns:
>>> +     * xmlNode - {<XMLNode>}
>>> +     */
>>> +    createFolderXML: function() {
>>> +        // Folder name
>>> +        var folderName = document.createElementNS(this.kmlns, "name");
>>> +        var folderNameText = document.createTextNode(this.foldersName);
>>> +        folderName.appendChild(folderNameText);
>>> +
>>> +        // Folder description
>>> +        var folderDesc = document.createElementNS(this.kmlns, 
>>> "description");       
>>> +        var folderDescText = document.createTextNode(this.foldersDesc);
>>> +        folderDesc.appendChild(folderDescText);
>>> +
>>> +        // Folder
>>> +        var folder = document.createElementNS(this.kmlns, "Folder");
>>> +        folder.appendChild(folderName);
>>> +        folder.appendChild(folderDesc);
>>> +       
>>> +        return folder;
>>> +    },
>>> +
>>> +    /**
>>> +     * Method: createPlacemarkXML
>>> +     * Accept an OpenLayers.Feature.Vector, and create a KML Placemark node
>>> +     *
>>> +     * Parameters:
>>> +     * feature - {<OpenLayers.Feature.Vector>}
>>> +     *
>>> +     * Returns:
>>> +     * xmlNode - {<XMLNode>}
>>> +     */
>>> +    createPlacemarkXML: function(feature) {
>>> +        // Placemark name
>>> +        var placemarkName = document.createElementNS(this.kmlns, "name");
>>> +        var placemarkNameText = document.createTextNode(feature.id);
>>> +        placemarkName.appendChild(placemarkNameText);
>>> +
>>> +        // Placemark description
>>> +        var placemarkDesc = document.createElementNS(this.kmlns, 
>>> "description");
>>> +        var placemarkDescText = 
>>> document.createTextNode(this.placemarksDesc);
>>> +        placemarkDesc.appendChild(placemarkDescText);
>>> +       
>>> +        // Placemark
>>> +        var placemarkNode = document.createElementNS(this.kmlns, 
>>> "Placemark");
>>> +        placemarkNode.appendChild(placemarkName);
>>> +        placemarkNode.appendChild(placemarkDesc);
>>> +
>>> +        // Geometry node (Point, LineString, etc. nodes)
>>> +        var geometryNode = this.buildGeometryNode(feature.geometry);
>>> +        placemarkNode.appendChild(geometryNode);       
>>> +       
>>> +        return placemarkNode;
>>> +    },   
>>> +
>>> +    /**
>>> +     * Method: buildGeometryNode
>>> +     * Builds a KML geometry node with a given geometry
>>> +     *
>>> +     * Parameters:
>>> +     * geometry - {<OpenLayers.Geometry>}
>>> +     *
>>> +     * Returns:
>>> +     * xmlNode - {<XMLNode>}
>>> +     */
>>> +    buildGeometryNode: function(geometry) {
>>> +    // TBD test if geoserver can be given a Multi-geometry for a 
>>> simple-geometry data store
>>> +    // ie if multipolygon can be sent for a polygon feature type
>>> +        var kml = "";
>>> +        // match MultiPolygon or Polygon
>>> +        if (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon"
>>> +            || geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
>>> +                kml = document.createElementNS(this.kmlns, 'Polygon');
>>> +               
>>> +                var outerRing = document.createElementNS(this.kmlns, 
>>> 'outerBoundaryIs');
>>> +                var linearRing = document.createElementNS(this.kmlns, 
>>> 'LinearRing');
>>> +               
>>> +                // TBD manage polygons with holes
>>> +                
>>> linearRing.appendChild(this.buildCoordinatesNode(geometry.components[0]));
>>> +                outerRing.appendChild(linearRing);
>>> +               
>>> +                kml.appendChild(outerRing);
>>> +            }
>>> +        // match MultiLineString or LineString
>>> +        else if (geometry.CLASS_NAME == 
>>> "OpenLayers.Geometry.MultiLineString"
>>> +                 || geometry.CLASS_NAME == 
>>> "OpenLayers.Geometry.LineString") {
>>> +                     kml = document.createElementNS(this.kmlns, 
>>> 'LineString');
>>> +                                         
>>> +                     kml.appendChild(this.buildCoordinatesNode(geometry));
>>> +                 }
>>> +        // match MultiPoint or Point
>>> +        else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point" ||
>>> +                  geometry.CLASS_NAME == 
>>> "OpenLayers.Geometry.MultiPoint") {                    
>>> +                kml = document.createElementNS(this.kmlns, 'Point');
>>> +
>>> +                // FIXME: There should be only one Point node per 
>>> Placemark node
>>> +                var parts = "";
>>> +                if (geometry.CLASS_NAME == 
>>> "OpenLayers.Geometry.MultiPoint") {
>>> +                    parts = geometry.components;
>>> +                } else {
>>> +                    parts = [geometry];
>>> +                }   
>>> +               
>>> +                for (var i = 0; i < parts.length; i++) {
>>> +                    kml.appendChild(this.buildCoordinatesNode(parts[i]));
>>> +               }    
>>> +        }
>>> +        return kml;        
>>> +    },
>>> +    /**
>>> +     * Method: buildCoordinatesNode
>>> +     * builds the KML coordinates node
>>> +     *
>>> +     * Parameters:
>>> +     * geometry - {<OpenLayers.Geometry>}
>>> +     *
>>> +     * Returns:
>>> +     * xmlNode - {<XMLNode>}
>>> +     */
>>> +    buildCoordinatesNode: function(geometry) {
>>> +        var coordinatesNode = document.createElementNS(this.kmlns, 
>>> "coordinates");
>>> +       
>>> +        var points = null;
>>> +        if (geometry.components) {
>>> +            points = geometry.components;
>>> +        }
>>> +
>>> +        var path = "";
>>> +        if (points) {
>>> +            for (var i = 0; i < points.length; i++) {
>>> +                path += points[i].x + "," + points[i].y + " ";
>>> +            }
>>> +        } else {
>>> +           path += geometry.x + "," + geometry.y + " ";
>>> +        }   
>>> +       
>>> +        var txtNode = document.createTextNode(path);
>>> +        coordinatesNode.appendChild(txtNode);
>>> +       
>>> +        return coordinatesNode;
>>> +    },   
>>> +
>>>      CLASS_NAME: "OpenLayers.Format.KML"
>>>  });    
>>>
>>>
>>> Tim Schaub wrote:
>>>     
>>>> Hi-
>>>>
>>>> So, we're mid-way through changing how XML flavors are read/written in 
>>>> OpenLayers.
>>>>
>>>> I've checked a new XML format in to the trunk.  This format has most of 
>>>> the methods we need for reading/writing XML cross-browser.
>>>>
>>>> To parse other XML flavors, format classes should be created that 
>>>> inherit from the XML format.
>>>>
>>>> For an (incomplete) example, see:
>>>> http://dev.openlayers.org/sandbox/tschaub/xml/lib/OpenLayers/Format/GML.js
>>>>
>>>> (The important part to notice in the above example is that the GML 
>>>> format inherits from the XML format.)
>>>>
>>>> Instead of using OpenLayers.Ajax methods or document methods, DOM 
>>>> creation and traversal should be done with OpenLayers.Format.XML 
>>>> methods.  I've created methods to reflect the W3C standard XML DOM 
>>>> methods - wrapping the standard ones and accommodating for non-compliant 
>>>> browsers.
>>>>
>>>> If you (or anyone) needs additional XML DOM methods, they should be 
>>>> added to the XML format.  The next addition (in my mind) is 
>>>> setAttributeNS.  Aside from that, you should be able to do efficient 
>>>> parsing and dom creation with the XML format methods.
>>>>
>>>> Please make modifications to the KML format in the trunk.  Create a 
>>>> ticket and attach patches in Trac.  I can offer review or other help as 
>>>> needed.
>>>>
>>>> I don't have time to keep these sandboxes entirely clean right now - so 
>>>> please forgive anything you find that is in a half-developed state.
>>>>
>>>> Tim
>>>>
>>>> PS - Eventually, I'll move the following example into the trunk.  It 
>>>> would be nice if we could demonstrate read/write capability for all 
>>>> vector formats in this way:
>>>> http://dev.openlayers.org/sandbox/tschaub/xml/examples/vector-formats.html
>>>>
>>>> (note again that this reflects partially complete work - and you should 
>>>> not be surprised to encounter errors or see changes at any time)
>>>>
>>>>
>>>> Damien Corpataux wrote:
>>>>   
>>>>       
>>>>> Hello,
>>>>>
>>>>> Here's what I did so far... Reading KML only work for KML 2.0 namespaces 
>>>>> for write() uses OpenLayers.Ajax.getElementsByTagNameNS(), but I'm 
>>>>> pretty sure it will work on future versions (2.1 parsing successful). 
>>>>> Also, GML and KML write() returns a Dom Document rather than a string. 
>>>>> Should one change the result type, breaking OpenLayers.Format interface, 
>>>>> or parse it into a string using OpenLayers.XML.....?
>>>>>
>>>>> Cheers,
>>>>> Damien
>>>>>
>>>>>     
>>>>>         
>>> [patch stripped]
>>>
>>> -- 
>>> Camptocamp SA
>>> Damien Corpataux
>>> PSE A
>>> CH-1015 Lausanne
>>> +41 21 619 10 22 (Direct)
>>> +41 21 619 10 10 (Centrale)
>>> +41 21 619 10 00 (Fax)
>>> P Please consider the environment
>>> Do you really need to print this email?
>>>
>>>
>>>
>>> ------------------------------------------------------------------------
>>>
>>> _______________________________________________
>>> Dev mailing list
>>> Dev at openlayers.org
>>> http://openlayers.org/mailman/listinfo/dev
>>>
>>>
>>> !DSPAM:4033,46cd662e41812090977483!
>>>     
>>
>> _______________________________________________
>> Dev mailing list
>> Dev at openlayers.org
>> http://openlayers.org/mailman/listinfo/dev
>>   
> 
> 
> -- 
> Camptocamp SA
> Damien Corpataux
> PSE A
> CH-1015 Lausanne
> +41 21 619 10 22 (Direct)
> +41 21 619 10 10 (Centrale)
> +41 21 619 10 00 (Fax)
> P Please consider the environment
> Do you really need to print this email?
> !DSPAM:4033,46dd6bc8182553362379201!




More information about the Dev mailing list