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

Damien Corpataux damien.corpataux at camptocamp.com
Thu Aug 23 06:48:49 EDT 2007


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?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.osgeo.org/pipermail/openlayers-dev/attachments/20070823/b2e67fc1/attachment.html


More information about the Dev mailing list