[OpenLayers-Commits] r4206 - in trunk/openlayers: lib/OpenLayers/Format tests/Format

commits at openlayers.org commits at openlayers.org
Mon Sep 10 18:02:19 EDT 2007


Author: tschaub
Date: 2007-09-10 18:02:18 -0400 (Mon, 10 Sep 2007)
New Revision: 4206

Modified:
   trunk/openlayers/lib/OpenLayers/Format/GML.js
   trunk/openlayers/tests/Format/test_GML.html
Log:
GML format rewrite - now subclasses from XML format.  Refactored code to get ~2-3x improvement in parsing time.  Thanks for all the tests crschmidt (closes #938).

Modified: trunk/openlayers/lib/OpenLayers/Format/GML.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Format/GML.js	2007-09-10 20:24:27 UTC (rev 4205)
+++ trunk/openlayers/lib/OpenLayers/Format/GML.js	2007-09-10 22:02:18 UTC (rev 4206)
@@ -3,78 +3,86 @@
  * for the full text of the license. */
 
 /**
- * @requires OpenLayers/Format.js
+ * @requires OpenLayers/Format/XML.js
  * @requires OpenLayers/Feature/Vector.js
- * @requires OpenLayers/Ajax.js
  * @requires OpenLayers/Geometry.js
  *
  * Class: OpenLayers.Format.GML
  * Read/Wite GML. Create a new instance with the <OpenLayers.Format.GML>
- *     constructor.
+ *     constructor.  Supports the GML simple features profile.
  * 
  * Inherits from:
  *  - <OpenLayers.Format>
  */
-OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format, {
+OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
     
     /*
      * APIProperty: featureNS
-     * Namespace used for feature attributes. Default matches the NS
-     * used by MapServer output.
+     * {String} Namespace used for feature attributes.  Default is
+     *     "http://mapserver.gis.umn.edu/mapserver".
      */
     featureNS: "http://mapserver.gis.umn.edu/mapserver",
     
+    /**
+     * APIProperty: featurePrefix
+     * {String} Namespace alias (or prefix) for feature nodes.  Default is
+     *     "feature".
+     */
+    featurePrefix: "feature",
+    
     /*
      * APIProperty: featureName
-     * element name for features. Default is 'featureMember'.
+     * {String} Element name for features. Default is "featureMember".
      */
     featureName: "featureMember", 
     
     /*
      * APIProperty: layerName
-     * Name of data layer. Default is 'features'.
+     * {String} Name of data layer. Default is "features".
      */
-     
     layerName: "features",
     
     /**
      * APIProperty: geometry
-     * Name of geometry element.
+     * {String} Name of geometry element.  Defaults to "geometry".
      */
     geometryName: "geometry",
     
     /** 
      * APIProperty: collectionName
-     * Name of featureCollection element
+     * {String} Name of featureCollection element.
      */
     collectionName: "FeatureCollection",
     
     /**
      * APIProperty: gmlns
-     * GML Namespace
+     * {String} GML Namespace.
      */
     gmlns: "http://www.opengis.net/gml",
-    
 
     /**
      * APIProperty: extractAttributes
-     * {Boolean} Extract attributes from GML. Most of the time, this is a
-     * significant time usage, due to the need to recursively descend the XML
-     * to search for attributes.
+     * {Boolean} Extract attributes from GML.
      */
     extractAttributes: true,
     
-    
     /**
      * Constructor: OpenLayers.Format.GML
-     * Create a new parser for GML
+     * Create a new parser for GML.
      *
      * Parameters:
      * options - {Object} An optional object whose properties will be set on
-     *                    this instance.
+     *     this instance.
      */
     initialize: function(options) {
-        OpenLayers.Format.prototype.initialize.apply(this, [options]);
+        // compile regular expressions once instead of every time they are used
+        this.regExes = {
+            trimSpace: (/^\s*|\s*$/g),
+            removeSpace: (/\s*/g),
+            splitSpace: (/\s+/),
+            trimComma: (/\s*,\s*/g)
+        };
+        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
     },
 
     /**
@@ -82,391 +90,617 @@
      * Read data from a string, and return a list of features. 
      * 
      * Parameters:
-     * data - {String} or {XMLNode} data to read/parse.
+     * data - {String} or {DOMElement} data to read/parse.
+     *
+     * Returns:
+     * {Array(<OpenLayers.Feature.Vector>)} An array of features.
      */
-     read: function(data) {
-        if (typeof data == "string") { 
-            data = OpenLayers.parseXMLString(data);
-        }    
-        var featureNodes = OpenLayers.Ajax.getElementsByTagNameNS(data, this.gmlns, "gml", this.featureName);
-        if (featureNodes.length == 0) { return []; }
-
-        // Determine dimension of the FeatureCollection. Ie, dim=2 means (x,y) coords
-        // dim=3 means (x,y,z) coords
-        // GML3 can have 2 or 3 dimensions. GML2 only 2.
-        var dim;
-        var coordNodes = OpenLayers.Ajax.getElementsByTagNameNS(featureNodes[0], this.gmlns, "gml", "posList");
-        if (coordNodes.length == 0) {
-            coordNodes = OpenLayers.Ajax.getElementsByTagNameNS(featureNodes[0], this.gmlns, "gml", "pos");
+    read: function(data) {
+        if(typeof data == "string") { 
+            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
         }
-        if (coordNodes.length > 0) {
-            dim = coordNodes[0].getAttribute("srsDimension");
-        }    
-        this.dim = (dim == "3" || dim == 3) ? 3 : 2;
-        
+        var featureNodes = this.getElementsByTagNameNS(data.documentElement,
+                                                       this.gmlns,
+                                                       this.featureName);
         var features = [];
-        
-        // Process all the featureMembers
-        for (var i = 0; i < featureNodes.length; i++) {
+        for(var i=0; i<featureNodes.length; i++) {
             var feature = this.parseFeature(featureNodes[i]);
-
-            if (feature) {
+            if(feature) {
                 features.push(feature);
             }
         }
         return features;
-     },
+    },
+    
+    /**
+     * Method: parseFeature
+     * This function is the core of the GML parsing code in OpenLayers.
+     *    It creates the geometries that are then attached to the returned
+     *    feature, and calls parseAttributes() to get attribute data out.
+     *    
+     * Parameters:
+     * node - {DOMElement} A GML feature node. 
+     */
+    parseFeature: function(node) {
+        // only accept on geometry per feature - look for highest "order"
+        var order = ["MultiPolygon", "Polygon",
+                     "MultiLineString", "LineString",
+                     "MultiPoint", "Point"];
+        var type, nodeList, geometry, parser;
+        for(var i=0; i<order.length; ++i) {
+            type = order[i];
+            nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
+            if(nodeList.length > 0) {
+                // only deal with first geometry of this type
+                var parser = this.parseGeometry[type.toLowerCase()];
+                if(parser) {
+                    geometry = parser.apply(this, [nodeList[0]]);
+                } else {
+                    OpenLayers.Console.error("Unsupported geometry type: " +
+                                             type);
+                }
+                // stop looking for different geometry types
+                break;
+            }
+        }
+        
+        // construct feature (optionally with attributes)
+        var attributes;
+        if(this.extractAttributes) {
+            attributes = this.parseAttributes(node);
+        }
+        var feature = new OpenLayers.Feature.Vector(geometry, attributes);
+        // assign fid - this can come from a "fid" or "id" attribute
+        var childNode = node.firstChild;
+        var fid;
+        while(childNode) {
+            if(childNode.nodeType == 1) {
+                fid = childNode.getAttribute("fid") ||
+                      childNode.getAttribute("id");
+                if(fid) {
+                    break;
+                }
+            }
+            childNode = childNode.nextSibling;
+        }
+        feature.fid = fid;
+        return feature;
+    },
+    
+    /**
+     * Property: parseGeometry
+     * Properties of this object are the functions that parse geometries based
+     *     on their type.
+     */
+    parseGeometry: {
+        
+        /**
+         * Method: parseGeometry.point
+         * Given a GML node representing a point geometry, create an OpenLayers
+         *     point geometry.
+         *
+         * Parameters:
+         * node - {DOMElement} A GML node.
+         *
+         * Returns:
+         * {<OpenLayers.Geometry.Point>} A point geometry.
+         */
+        point: function(node) {
+            /**
+             * Three coordinate variations to consider:
+             * 1) <gml:pos>x y z</gml:pos>
+             * 2) <gml:coordinates>x, y, z</gml:coordinates>
+             * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
+             */
+            var nodeList;
+            var coords = [];
 
-     /**
-      * Method: parseFeature
-      * This function is the core of the GML parsing code in OpenLayers.
-      * It creates the geometries that are then attached to the returned
-      * feature, and calls parseAttributes() to get attribute data out.
-     
-      * Parameters:
-      * xmlNode - {<DOMElement>} 
-      */
-     parseFeature: function(xmlNode) {
-        var geom;
-        var p; // [points,bounds]
+            // look for <gml:pos>
+            var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
+            if(nodeList.length > 0) {
+                coordString = nodeList[0].firstChild.nodeValue;
+                coordString = coordString.replace(this.regExes.trimSpace, "");
+                coords = coordString.split(this.regExes.splitSpace);
+            }
 
-        var feature = new OpenLayers.Feature.Vector();
+            // look for <gml:coordinates>
+            if(coords.length == 0) {
+                nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+                                                       "coordinates");
+                if(nodeList.length > 0) {
+                    coordString = nodeList[0].firstChild.nodeValue;
+                    coordString = coordString.replace(this.regExes.removeSpace,
+                                                      "");
+                    coords = coordString.split(",");
+                }
+            }
 
-        // match MultiPolygon
-        if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.gmlns, "gml", "MultiPolygon").length != 0) {
-            var multipolygon = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.gmlns, "gml", "MultiPolygon")[0];
-            feature.fid = multipolygon.parentNode.parentNode.getAttribute('fid');
-
-            geom = new OpenLayers.Geometry.MultiPolygon();
-            var polygons = OpenLayers.Ajax.getElementsByTagNameNS(multipolygon,
-                this.gmlns, "gml", "Polygon");
-            for (var i = 0; i < polygons.length; i++) {
-                polygon = this.parsePolygonNode(polygons[i],geom);
-                geom.addComponents(polygon);
-            }
-        }
-        // match MultiLineString
-        else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
-            this.gmlns, "gml", "MultiLineString").length != 0) {
-            var multilinestring = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
-            this.gmlns, "gml", "MultiLineString")[0];
-            feature.fid = multilinestring.parentNode.parentNode.getAttribute('fid');
-            
-            geom = new OpenLayers.Geometry.MultiLineString();
-            var lineStrings = OpenLayers.Ajax.getElementsByTagNameNS(multilinestring, this.gmlns, "gml", "LineString");
-            
-            for (var i = 0; i < lineStrings.length; i++) {
-                p = this.parseCoords(lineStrings[i]);
-                if(p.points){
-                    var lineString = new OpenLayers.Geometry.LineString(p.points);
-                    geom.addComponents(lineString);
-                    // TBD Bounds only set for one of multiple geometries
+            // look for <gml:coord>
+            if(coords.length == 0) {
+                nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+                                                       "coord");
+                if(nodeList.length > 0) {
+                    var xList = this.getElementsByTagNameNS(nodeList[0],
+                                                            this.gmlns, "X");
+                    var yList = this.getElementsByTagNameNS(nodeList[0],
+                                                            this.gmlns, "Y");
+                    if(xList.length > 0 && yList.length > 0) {
+                        coords = [xList[0].firstChild.nodeValue,
+                                  yList[0].firstChild.nodeValue];
+                    }
                 }
             }
-        }
-        // match MultiPoint
-        else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
-            this.gmlns, "gml", "MultiPoint").length != 0) {
-            var multiPoint = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
-                this.gmlns, "gml", "MultiPoint")[0];
-            feature.fid = multiPoint.parentNode.parentNode.getAttribute('fid');
                 
-            geom = new OpenLayers.Geometry.MultiPoint();
-            
-            var points = OpenLayers.Ajax.getElementsByTagNameNS(multiPoint, this.gmlns, "gml", "Point");
-            
-            for (var i = 0; i < points.length; i++) {
-                p = this.parseCoords(points[i]);
-                geom.addComponents(p.points[0]);
-                // TBD Bounds only set for one of multiple geometries
+            // preserve third dimension
+            if(coords.length == 2) {
+                coords[2] = null;
             }
-        }
-        // match Polygon
-        else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
-            this.gmlns, "gml", "Polygon").length != 0) {
-            var polygon = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
-                this.gmlns, "gml", "Polygon")[0];
-            feature.fid = polygon.parentNode.parentNode.getAttribute('fid');
-            
-            geom = this.parsePolygonNode(polygon);
-        }
-        // match LineString
-        else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
-            this.gmlns, "gml", "LineString").length != 0) {
-            var lineString = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
-                this.gmlns, "gml", "LineString")[0];
-            feature.fid = lineString.parentNode.parentNode.getAttribute('fid');
+            return new OpenLayers.Geometry.Point(coords[0], coords[1],
+                                                 coords[2]);
+        },
+        
+        /**
+         * Method: parseGeometry.multipoint
+         * Given a GML node representing a multipoint geometry, create an
+         *     OpenLayers multipoint geometry.
+         *
+         * Parameters:
+         * node - {DOMElement} A GML node.
+         *
+         * Returns:
+         * {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
+         */
+        multipoint: function(node) {
+            var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+                                                       "Point");
+            var components = [];
+            if(nodeList.length > 0) {
+                var point;
+                for(var i=0; i<nodeList.length; ++i) {
+                    point = this.parseGeometry.point.apply(this, [nodeList[i]]);
+                    if(point) {
+                        components.push(point);
+                    }
+                }
+            }
+            return new OpenLayers.Geometry.MultiPoint(components);
+        },
+        
+        /**
+         * Method: parseGeometry.linestring
+         * Given a GML node representing a linestring geometry, create an
+         *     OpenLayers linestring geometry.
+         *
+         * Parameters:
+         * node - {DOMElement} A GML node.
+         *
+         * Returns:
+         * {<OpenLayers.Geometry.LineString>} A linestring geometry.
+         */
+        linestring: function(node, ring) {
+            /**
+             * Two coordinate variations to consider:
+             * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
+             * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
+             */
+            var nodeList, coordString;
+            var coords = [];
+            var points = [];
 
-            p = this.parseCoords(lineString);
-            if (p.points) {
-                geom = new OpenLayers.Geometry.LineString(p.points);
-                // TBD Bounds only set for one of multiple geometries
+            // look for <gml:posList>
+            nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
+            if(nodeList.length > 0) {
+                coordString = nodeList[0].firstChild.nodeValue;
+                coordString = coordString.replace(this.regExes.trimSpace, "");
+                coords = coordString.split(this.regExes.splitSpace);
+                var dim = parseInt(nodeList[0].getAttribute("dimension"));
+                var j, x, y, z;
+                for(var i=0; i<coords.length/dim; ++i) {
+                    j = i * dim;
+                    x = coords[j];
+                    y = coords[j+1];
+                    z = (dim == 2) ? null : coords[j+2];
+                    points.push(new OpenLayers.Geometry.Point(x, y, z));
+                }
             }
-        }
-        // match Point
-        else if (OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
-            this.gmlns, "gml", "Point").length != 0) {
-            var point = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode,
-                this.gmlns, "gml", "Point")[0];
-            feature.fid = point.parentNode.parentNode.getAttribute('fid');
-            
-            p = this.parseCoords(point);
-            if (p.points) {
-                geom = p.points[0];
-                // TBD Bounds only set for one of multiple geometries
+
+            // look for <gml:coordinates>
+            if(coords.length == 0) {
+                nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+                                                       "coordinates");
+                if(nodeList.length > 0) {
+                    coordString = nodeList[0].firstChild.nodeValue;
+                    coordString = coordString.replace(this.regExes.trimSpace,
+                                                      "");
+                    coordString = coordString.replace(this.regExes.trimComma,
+                                                      ",");
+                    var pointList = coordString.split(this.regExes.splitSpace);
+                    for(var i=0; i<pointList.length; ++i) {
+                        coords = pointList[i].split(",");
+                        if(coords.length == 2) {
+                            coords[2] = null;
+                        }
+                        points.push(new OpenLayers.Geometry.Point(coords[0],
+                                                                  coords[1],
+                                                                  coords[2]));
+                    }
+                }
             }
-        }
+
+            var line = null;
+            if(points.length != 0) {
+                if(ring) {
+                    line = new OpenLayers.Geometry.LinearRing(points);
+                } else {
+                    line = new OpenLayers.Geometry.LineString(points);
+                }
+            }
+            return line;
+        },
         
-        feature.geometry = geom; 
-        if (this.extractAttributes) {
-            feature.attributes = this.parseAttributes(xmlNode);
-        }    
+        /**
+         * Method: parseGeometry.multilinestring
+         * Given a GML node representing a multilinestring geometry, create an
+         *     OpenLayers multilinestring geometry.
+         *
+         * Parameters:
+         * node - {DOMElement} A GML node.
+         *
+         * Returns:
+         * {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
+         */
+        multilinestring: function(node) {
+            var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+                                                       "LineString");
+            var components = [];
+            if(nodeList.length > 0) {
+                var line;
+                for(var i=0; i<nodeList.length; ++i) {
+                    line = this.parseGeometry.linestring.apply(this,
+                                                               [nodeList[i]]);
+                    if(line) {
+                        components.push(line);
+                    }
+                }
+            }
+            return new OpenLayers.Geometry.MultiLineString(components);
+        },
         
-        return feature;
-    },        
-    
-    /**
-     * Method: parseAttributes
-     * recursive function parse the attributes of a GML node.
-     * Searches for any child nodes which aren't geometries,
-     * and gets their value.
-     *
-     * Parameters:
-     * xmlNode - {<DOMElement>} 
-     */
-    parseAttributes: function(xmlNode) {
-        var nodes = xmlNode.childNodes;
-        var attributes = {};
-        for(var i = 0; i < nodes.length; i++) {
-            var name = nodes[i].nodeName;
-            var value = OpenLayers.Util.getXmlNodeValue(nodes[i]);
-            // Ignore Geometry attributes
-            // match ".//gml:pos|.//gml:posList|.//gml:coordinates"
-            if((name.search(":pos")!=-1)
-              ||(name.search(":posList")!=-1)
-              ||(name.search(":coordinates")!=-1)){
-               continue;    
+        /**
+         * Method: parseGeometry.polygon
+         * Given a GML node representing a polygon geometry, create an
+         *     OpenLayers polygon geometry.
+         *
+         * Parameters:
+         * node - {DOMElement} A GML node.
+         *
+         * Returns:
+         * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
+         */
+        polygon: function(node) {
+            var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+                                                       "LinearRing");
+            var components = [];
+            if(nodeList.length > 0) {
+                // this assumes exterior ring first, inner rings after
+                var ring;
+                for(var i=0; i<nodeList.length; ++i) {
+                    ring = this.parseGeometry.linestring.apply(this,
+                                                        [nodeList[i], true]);
+                    if(ring) {
+                        components.push(ring);
+                    }
+                }
             }
-            
-            // Check for a leaf node
-            if((nodes[i].childNodes.length == 1 && nodes[i].childNodes[0].nodeName == "#text")
-                || (nodes[i].childNodes.length == 0 && nodes[i].nodeName!="#text")) {
-                attributes[name] = value;
+            return new OpenLayers.Geometry.Polygon(components);
+        },
+        
+        /**
+         * Method: parseGeometry.multipolygon
+         * Given a GML node representing a multipolygon geometry, create an
+         *     OpenLayers multipolygon geometry.
+         *
+         * Parameters:
+         * node - {DOMElement} A GML node.
+         *
+         * Returns:
+         * {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry.
+         */
+        multipolygon: function(node) {
+            var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
+                                                       "Polygon");
+            var components = [];
+            if(nodeList.length > 0) {
+                var polygon;
+                for(var i=0; i<nodeList.length; ++i) {
+                    polygon = this.parseGeometry.polygon.apply(this,
+                                                               [nodeList[i]]);
+                    if(polygon) {
+                        components.push(polygon);
+                    }
+                }
             }
-            OpenLayers.Util.extend(attributes, this.parseAttributes(nodes[i]))
-        }   
-        return attributes;
-    },
-    
-    /**
-     * Method: parsePolygonNode
-     * 
-     * Parameters:
-     * xmlNode - {XMLNode} 
-     *
-     * Returns:
-     * {<OpenLayers.Geometry.Polygon>} polygon geometry
-     */
-    parsePolygonNode: function(polygonNode) {
-        var linearRings = OpenLayers.Ajax.getElementsByTagNameNS(polygonNode,
-            this.gmlns, "gml", "LinearRing");
-        
-        var rings = [];
-        var p;
-        var polyBounds;
-        for (var i = 0; i < linearRings.length; i++) {
-            p = this.parseCoords(linearRings[i]);
-            ring1 = new OpenLayers.Geometry.LinearRing(p.points);
-            rings.push(ring1);
+            return new OpenLayers.Geometry.MultiPolygon(components);
         }
-        
-        var poly = new OpenLayers.Geometry.Polygon(rings);
-        return poly;
     },
     
     /**
-     * Method: parseCoords
-     * Extract Geographic coordinates from an XML node.
+     * Method: parseAttributes
      *
      * Parameters:
-     * xmlNode - {<XMLNode>} 
+     * node - {<DOMElement>}
      *
      * Returns:
-     * An array of <OpenLayers.Geometry.Point> points.
+     * {Object} An attributes object.
      */
-    parseCoords: function(xmlNode) {
-        var x, y, left, bottom, right, top, bounds;
-        var p = []; // return value = [points,bounds]
-        
-        if (xmlNode) {
-            p.points = [];
-            
-            // match ".//gml:pos|.//gml:posList|.//gml:coordinates"
-            // Note: GML2 coordinates are of the form:x y,x y,x y
-            // GML2 can also be of the form <coord><x>1</x><y>2</y></coord>
-            //       GML3 posList is of the form:x y x y. OR x y z x y z.
-            
-            // GML3 Line or Polygon
-            var coordNodes = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.gmlns, "gml", "posList");
-            
-            // GML3 Point
-            if (coordNodes.length == 0) { 
-                coordNodes = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.gmlns, "gml", "pos");
-            }    
-
-            // GML2
-            if (coordNodes.length == 0) {
-                coordNodes = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, this.gmlns, "gml", "coordinates");
-            }    
-
-            // TBD: Need to handle an array of coordNodes not just coordNodes[0]
-            
-            var coordString = OpenLayers.Util.getXmlNodeValue(coordNodes[0]);
-            
-            // Extract an array of Numbers from CoordString
-            var nums = (coordString) ? coordString.split(/[, \n\t]+/) : [];
-            
-            // Remove elements caused by leading and trailing white space
-            while (nums[0] == "") 
-                nums.shift();
-            
-            while (nums[nums.length-1] == "") 
-                nums.pop();
-            
-            for(var i = 0; i < nums.length; i = i + this.dim) {
-                x = parseFloat(nums[i]);
-                y = parseFloat(nums[i+1]);
-                p.points.push(new OpenLayers.Geometry.Point(x, y));
+    parseAttributes: function(node) {
+        var attributes = {};
+        // assume attributes are children of the first type 1 child
+        var childNode = node.firstChild;
+        var children, i, child, grandchildren, grandchild, name, value;
+        while(childNode) {
+            if(childNode.nodeType == 1) {
+                // attributes are type 1 children with one type 3 child
+                children = childNode.childNodes;
+                for(i=0; i<children.length; ++i) {
+                    child = children[i];
+                    if(child.nodeType == 1) {
+                        grandchildren = child.childNodes;
+                        if(grandchildren.length == 1) {
+                            grandchild = grandchildren[0];
+                            if(grandchild.nodeType == 3) {
+                                name = (child.prefix) ?
+                                        child.nodeName.split(":")[1] :
+                                        child.nodeName;
+                                value = grandchild.nodeValue.replace(
+                                                this.regExes.trimSpace, "");
+                                attributes[name] = value;
+                            }
+                        }
+                    }
+                }
+                break;
             }
+            childNode = childNode.nextSibling;
         }
-        return p;
+        return attributes;
     },
     
     /**
      * APIMethod: write
-     * Accept Feature Array, and return a string. 
+     * Generate a GML document string given a list of features. 
      * 
      * Parameters:
-     * features - Array({<OpenLayers.Feature.Vector>}> List of features to
-     * serialize into a string.
+     * features - {Array(<OpenLayers.Feature.Vector>)} List of features to
+     *     serialize into a string.
+     *
+     * Returns:
+     * {String} A string representing the GML document.
      */
-     write: function(features) {
-        var featureCollection = document.createElementNS("http://www.opengis.net/wfs", "wfs:" + this.collectionName);
-        for (var i=0; i < features.length; i++) {
-            featureCollection.appendChild(this.createFeatureXML(features[i]));
+    write: function(features) {
+        if(!(features instanceof Array)) {
+            features = [features];
         }
-        return featureCollection;
-     },
-    
+        var gml = this.createElementNS("http://www.opengis.net/wfs",
+                                       "wfs:" + this.collectionName);
+        for(var i=0; i<features.length; i++) {
+            gml.appendChild(this.createFeatureXML(features[i]));
+        }
+        return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
+    },
+
     /** 
      * Method: createFeatureXML
-     * Accept an OpenLayers.Feature.Vector, and build a geometry for it.
+     * Accept an OpenLayers.Feature.Vector, and build a GML node for it.
      *
      * Parameters:
-     * feature - {<OpenLayers.Feature.Vector>} 
+     * feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML.
      *
      * Returns:
-     * {DOMElement}
+     * {DOMElement} A node reprensting the feature in GML.
      */
     createFeatureXML: function(feature) {
-        var geometryNode = this.buildGeometryNode(feature.geometry);
-        var geomContainer = document.createElementNS(this.featureNS, "feature:"+this.geometryName);
+        var geometry = feature.geometry;
+        var geometryNode = this.buildGeometryNode(geometry);
+        var geomContainer = this.createElementNS(this.featureNS,
+                                                 this.featurePrefix + ":" +
+                                                 this.geometryName);
         geomContainer.appendChild(geometryNode);
-        var featureNode = document.createElementNS(this.gmlns, "gml:" + this.featureName);
-        var featureContainer = document.createElementNS(this.featureNS, "feature:"+this.layerName);
+        var featureNode = this.createElementNS(this.gmlns,
+                                               "gml:" + this.featureName);
+        var featureContainer = this.createElementNS(this.featureNS,
+                                                    this.featurePrefix + ":" +
+                                                    this.layerName);
+        var fid = feature.fid || feature.id;
+        featureContainer.setAttribute("fid", fid);
         featureContainer.appendChild(geomContainer);
         for(var attr in feature.attributes) {
-            var attrText = document.createTextNode(feature.attributes[attr]); 
-            var nodename = attr;
-            if (attr.search(":") != -1) {
-                nodename = attr.split(":")[1];
-            }    
-            var attrContainer = document.createElementNS(this.featureNS, "feature:"+nodename);
+            var attrText = this.createTextNode(feature.attributes[attr]); 
+            var nodename = attr.substring(attr.lastIndexOf(":") + 1);
+            var attrContainer = this.createElementNS(this.featureNS,
+                                                     this.featurePrefix + ":" +
+                                                     nodename);
             attrContainer.appendChild(attrText);
             featureContainer.appendChild(attrContainer);
         }    
         featureNode.appendChild(featureContainer);
         return featureNode;
-    },    
+    },
     
     /**
-     * Method: buildGeometryNode 
-     * builds a GML file with a given geometry
-     *
-     * Parameters:
-     * geometry - {<OpenLayers.Geometry>} 
+     * APIMethod: buildGeometryNode
      */
     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 gml = "";
-        // match MultiPolygon or Polygon
-        if (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon"
-            || geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
-                gml = document.createElementNS(this.gmlns, 'gml:MultiPolygon');
-                
-                // TBD retrieve the srs from layer
-                // srsName is non-standard, so not including it until it's right.
-                //gml.setAttribute("srsName", "http://www.opengis.net/gml/srs/epsg.xml#4326");
-                
-                var polygonMember = document.createElementNS(this.gmlns, 'gml:polygonMember');
-                
-                var polygon = document.createElementNS(this.gmlns, 'gml:Polygon');
-                var outerRing = document.createElementNS(this.gmlns, 'gml:outerBoundaryIs');
-                var linearRing = document.createElementNS(this.gmlns, 'gml:LinearRing');
-                
-                // TBD manage polygons with holes
-                linearRing.appendChild(this.buildCoordinatesNode(geometry.components[0]));
-                outerRing.appendChild(linearRing);
-                polygon.appendChild(outerRing);
-                polygonMember.appendChild(polygon);
-                
-                gml.appendChild(polygonMember);
+        var className = geometry.CLASS_NAME;
+        var type = className.substring(className.lastIndexOf(".") + 1);
+        var builder = this.buildGeometry[type.toLowerCase()];
+        return builder.apply(this, [geometry]);
+    },
+
+    /**
+     * Property: buildGeometry
+     * Object containing methods to do the actual geometry node building
+     *     based on geometry type.
+     */
+    buildGeometry: {
+        // TBD retrieve the srs from layer
+        // srsName is non-standard, so not including it until it's right.
+        // gml.setAttribute("srsName",
+        //                  "http://www.opengis.net/gml/srs/epsg.xml#4326");
+
+        /**
+         * Method: buildGeometry.point
+         * Given an OpenLayers point geometry, create a GML point.
+         *
+         * Parameters:
+         * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
+         *
+         * Returns:
+         * {DOMElement} A GML point node.
+         */
+        point: function(geometry) {
+            var gml = this.createElementNS(this.gmlns, "gml:Point");
+            gml.appendChild(this.buildCoordinatesNode(geometry));
+            return gml;
+        },
+        
+        /**
+         * Method: buildGeometry.multipoint
+         * Given an OpenLayers multipoint geometry, create a GML multipoint.
+         *
+         * Parameters:
+         * geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
+         *
+         * Returns:
+         * {DOMElement} A GML multipoint node.
+         */
+        multipoint: function(geometry) {
+            var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
+            var points = geometry.components;
+            var pointMember, pointGeom;
+            for(var i=0; i<points.length; i++) { 
+                pointMember = this.createElementNS(this.gmlns,
+                                                   "gml:pointMember");
+                pointGeom = this.buildGeometry.point.apply(this,
+                                                               [points[i]]);
+                pointMember.appendChild(pointGeom);
+                gml.appendChild(pointMember);
             }
-        // match MultiLineString or LineString
-        else if (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString"
-                 || geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
-                     gml = document.createElementNS(this.gmlns, 'gml:MultiLineString');
-                     
-                     // TBD retrieve the srs from layer
-                     // srsName is non-standard, so not including it until it's right.
-                     //gml.setAttribute("srsName", "http://www.opengis.net/gml/srs/epsg.xml#4326");
-                     
-                     var lineStringMember = document.createElementNS(this.gmlns, 'gml:lineStringMember');
-                     
-                     var lineString = document.createElementNS(this.gmlns, 'gml:LineString');
-                     
-                     lineString.appendChild(this.buildCoordinatesNode(geometry));
-                     lineStringMember.appendChild(lineString);
-                     
-                     gml.appendChild(lineStringMember);
-                 }
-        // match MultiPoint or Point
-        else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point" || 
-                  geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") {
-                // TBD retrieve the srs from layer
-                // srsName is non-standard, so not including it until it's right.
-                //gml.setAttribute("srsName", "http://www.opengis.net/gml/srs/epsg.xml#4326");
-                     
-                gml = document.createElementNS(this.gmlns, 'gml:MultiPoint');
-                var parts = "";
-                if (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") {
-                    parts = geometry.components;
-                } else {
-                    parts = [geometry];
-                }    
-                
-                for (var i = 0; i < parts.length; i++) { 
-                    var pointMember = document.createElementNS(this.gmlns, 'gml:pointMember');
-                    var point = document.createElementNS(this.gmlns, 'gml:Point');
-                    point.appendChild(this.buildCoordinatesNode(parts[i]));
-                    pointMember.appendChild(point);
-                    gml.appendChild(pointMember);
-               }     
+            return gml;            
+        },
+        
+        /**
+         * Method: buildGeometry.linestring
+         * Given an OpenLayers linestring geometry, create a GML linestring.
+         *
+         * Parameters:
+         * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
+         *
+         * Returns:
+         * {DOMElement} A GML linestring node.
+         */
+        linestring: function(geometry) {
+            var gml = this.createElementNS(this.gmlns, "gml:LineString");
+            gml.appendChild(this.buildCoordinatesNode(geometry));
+            return gml;
+        },
+        
+        /**
+         * Method: buildGeometry.multilinestring
+         * Given an OpenLayers multilinestring geometry, create a GML
+         *     multilinestring.
+         *
+         * Parameters:
+         * geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring
+         *     geometry.
+         *
+         * Returns:
+         * {DOMElement} A GML multilinestring node.
+         */
+        multilinestring: function(geometry) {
+            var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
+            var lines = geometry.components;
+            var lineMember, lineGeom;
+            for(var i=0; i<lines.length; ++i) {
+                lineMember = this.createElementNS(this.gmlns,
+                                                  "gml:lineStringMember");
+                lineGeom = this.buildGeometry.linestring.apply(this,
+                                                                   [lines[i]]);
+                lineMember.appendChild(lineGeom);
+                gml.appendChild(lineMember);
+            }
+            return gml;
+        },
+        
+        /**
+         * Method: buildGeometry.linearring
+         * Given an OpenLayers linearring geometry, create a GML linearring.
+         *
+         * Parameters:
+         * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
+         *
+         * Returns:
+         * {DOMElement} A GML linearring node.
+         */
+        linearring: function(geometry) {
+            var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
+            gml.appendChild(this.buildCoordinatesNode(geometry));
+            return gml;
+        },
+        
+        /**
+         * Method: buildGeometry.polygon
+         * Given an OpenLayers polygon geometry, create a GML polygon.
+         *
+         * Parameters:
+         * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
+         *
+         * Returns:
+         * {DOMElement} A GML polygon node.
+         */
+        polygon: function(geometry) {
+            var gml = this.createElementNS(this.gmlns, "gml:Polygon");
+            var rings = geometry.components;
+            var ringMember, ringGeom, type;
+            for(var i=0; i<rings.length; ++i) {
+                type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
+                ringMember = this.createElementNS(this.gmlns,
+                                                  "gml:" + type);
+                ringGeom = this.buildGeometry.linearring.apply(this,
+                                                                   [rings[i]]);
+                ringMember.appendChild(ringGeom);
+                gml.appendChild(ringMember);
+            }
+            return gml;
+        },
+        
+        /**
+         * Method: buildGeometry.multipolygon
+         * Given an OpenLayers multipolygon geometry, create a GML multipolygon.
+         *
+         * Parameters:
+         * geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon
+         *     geometry.
+         *
+         * Returns:
+         * {DOMElement} A GML multipolygon node.
+         */
+        multipolygon: function(geometry) {
+            var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
+            var polys = geometry.components;
+            var polyMember, polyGeom;
+            for(var i=0; i<polys.length; ++i) {
+                polyMember = this.createElementNS(this.gmlns,
+                                                  "gml:polygonMember");
+                polyGeom = this.buildGeometry.polygon.apply(this,
+                                                                [polys[i]]);
+                polyMember.appendChild(polyGeom);
+                gml.appendChild(polyMember);
+            }
+            return gml;
         }
-        return gml;         
     },
-     
+
     /**
      * Method: buildCoordinates
      * builds the coordinates XmlNode
@@ -479,30 +713,23 @@
      * {XmlNode} created xmlNode
      */
     buildCoordinatesNode: function(geometry) {
-        var coordinatesNode = document.createElementNS(this.gmlns, "gml:coordinates");
+        var coordinatesNode = this.createElementNS(this.gmlns,
+                                                   "gml:coordinates");
         coordinatesNode.setAttribute("decimal", ".");
         coordinatesNode.setAttribute("cs", ",");
         coordinatesNode.setAttribute("ts", " ");
         
-        var points = null;
-        if (geometry.components) {
-            points = geometry.components;
+        var points = (geometry.components) ? geometry.components : [geometry];
+        var parts = [];
+        for(var i=0; i<points.length; i++) {
+            parts.push(points[i].x + "," + points[i].y);
         }
 
-        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);
+        var txtNode = this.createTextNode(parts.join(" "));
         coordinatesNode.appendChild(txtNode);
         
         return coordinatesNode;
     },
 
     CLASS_NAME: "OpenLayers.Format.GML" 
-});     
+});

Modified: trunk/openlayers/tests/Format/test_GML.html
===================================================================
--- trunk/openlayers/tests/Format/test_GML.html	2007-09-10 20:24:27 UTC (rev 4205)
+++ trunk/openlayers/tests/Format/test_GML.html	2007-09-10 22:02:18 UTC (rev 4206)
@@ -3,7 +3,158 @@
     <script src="../../lib/OpenLayers.js"></script> 
     <script type="text/javascript">
 
-    var test_content = '<wfs:FeatureCollection' + 
+    function test_Format_GML_constructor(t) { 
+        t.plan(4); 
+         
+        var options = {'foo': 'bar'}; 
+        var format = new OpenLayers.Format.GML(options); 
+        t.ok(format instanceof OpenLayers.Format.GML, 
+             "new OpenLayers.Format.GML returns object" ); 
+        t.eq(format.foo, "bar", "constructor sets options correctly"); 
+        t.eq(typeof format.read, "function", "format has a read function"); 
+        t.eq(typeof format.write, "function", "format has a write function"); 
+    } 
+    function test_Format_GML_getFid(t) { 
+        t.plan(2);
+        var parser = new OpenLayers.Format.GML();
+        data = parser.read(test_content[1]);
+        t.eq(data[0].fid, '221', 'fid on polygons set correctly (with whitespace)');
+        t.eq(data[1].fid, '8', 'fid on linestrings set correctly with whitespace'); 
+    }
+    function test_Format_GML_no_clobber(t) { 
+        t.plan(1); 
+        var parser = new OpenLayers.Format.GML(); 
+        data = parser.read(test_content[1]); 
+        t.eq(window.i, undefined,
+             "i is undefined in window scope after reading."); 
+    }
+    function test_Format_GML_read_3d(t) {
+        t.plan(2);
+        var parser = new OpenLayers.Format.GML();
+        data = parser.read(test_content[0]);
+        t.eq(data[0].geometry.getBounds().toBBOX(), "-1254041.389712,250906.951598,-634517.119991,762236.29408", "Reading 3d content returns geometry with correct bounds (no 0,0)");
+        t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Reading 3d content returns polygon geometry");
+    }   
+    function test_Format_GML_write_geoms(t) {
+        t.plan(5);
+        var parser = new OpenLayers.Format.GML();
+        
+        var point = shell_start + serialize_geoms['point'] + shell_end;
+        data = parser.read(point);
+        var output = parser.write(data);
+        t.eq(output, point, "Point geometry round trips correctly.");
+        
+        var linestring = shell_start + serialize_geoms['linestring'] + shell_end;
+        data = parser.read(linestring);
+        var output = parser.write(data);
+        t.eq(output, linestring, "Line geometry round trips correctly.");
+        
+        var polygon = shell_start + serialize_geoms['polygon'] + shell_end;
+        data = parser.read(polygon);
+        var output = parser.write(data);
+        t.eq(output, polygon, "Poly geometry round trips correctly.");
+        
+        var multipoint = shell_start + serialize_geoms['multipoint'] + shell_end;
+        data = parser.read(multipoint);
+        var output = parser.write(data);
+        t.eq(output, multipoint, "MultiPoint geometry round trips correctly.");
+        
+        var multilinestring = shell_start + serialize_geoms['multilinestring'] + shell_end;
+        data = parser.read(multilinestring);
+        var output = parser.write(data);
+        t.eq(output, multilinestring, "MultiLine geometry round trips correctly.");
+    }    
+    function test_Format_GML_read_point_geom(t) {
+        t.plan(3);
+        
+        var point = shell_start + geoms['point'] + shell_end;
+        var parser = new OpenLayers.Format.GML();
+        data = parser.read(point);
+        t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Point", "Point GML returns correct classname");
+        t.eq(data[0].geometry.x, 1, "x coord correct");
+        t.eq(data[0].geometry.y, 2, "y coord correct");
+    }    
+    function test_Format_GML_read_linestring_geom(t) {
+        t.plan(5);
+        
+        var line = shell_start + geoms['linestring'] + shell_end;
+        var parser = new OpenLayers.Format.GML();
+        data = parser.read(line);
+        t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.LineString", "LineString GML returns correct classname");
+        t.eq(data[0].geometry.components[0].x, 1, "first x coord correct");
+        t.eq(data[0].geometry.components[0].y, 2, "first y coord correct");
+        t.eq(data[0].geometry.components[1].x, 4, "second x coord correct");
+        t.eq(data[0].geometry.components[1].y, 5, "second y coord correct");
+    }    
+    function test_Format_GML_read_polygon_geom(t) {
+        t.plan(7);
+        
+        var polygon = shell_start + geoms['polygon'] + shell_end;
+        var parser = new OpenLayers.Format.GML();
+        data = parser.read(polygon);
+        t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Polygon GML returns correct classname");
+        t.eq(data[0].geometry.components[0].components[0].x, 1, "first x coord correct");
+        t.eq(data[0].geometry.components[0].components[0].y, 2, "first y coord correct");
+        t.eq(data[0].geometry.components[0].components[1].x, 4, "second x coord correct");
+        t.eq(data[0].geometry.components[0].components[1].y, 5, "second y coord correct");
+        t.eq(data[0].geometry.components[0].components.length, 4, "coords length correct");
+        t.eq(data[0].geometry.components.length, 1, "rings length correct");
+    }   
+    function test_Format_GML_read_multipoint_geom(t) {
+        t.plan(6);
+        
+        var multipoint = shell_start + geoms['multipoint'] + shell_end;
+        var parser = new OpenLayers.Format.GML();
+        data = parser.read(multipoint);
+        t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.MultiPoint", "MultiPoint GML returns correct classname");
+        t.eq(data[0].geometry.components[0].x, 1, "x coord correct");
+        t.eq(data[0].geometry.components[0].y, 2, "y coord correct");
+        t.eq(data[0].geometry.components.length, 2, "length correct");
+        t.eq(data[0].geometry.components[1].x, 4, "x coord correct");
+        t.eq(data[0].geometry.components[1].y, 5, "y coord correct");
+    }    
+    function test_Format_GML_read_multilinestring_geom(t) {
+        t.plan(6);
+        
+        var multilinestring = shell_start + geoms['multilinestring'] + shell_end;
+        var parser = new OpenLayers.Format.GML();
+        data = parser.read(multilinestring);
+        t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.MultiLineString", "MultiLineString GML returns correct classname");
+        t.eq(data[0].geometry.components[0].components[0].x, 1, "x coord correct");
+        t.eq(data[0].geometry.components[0].components[0].y, 2, "y coord correct");
+        t.eq(data[0].geometry.components[0].components.length, 2, "length correct");
+        t.eq(data[0].geometry.components[0].components[1].x, 4, "x coord correct");
+        t.eq(data[0].geometry.components[0].components[1].y, 5, "y coord correct");
+        
+    }    
+    function test_Format_GML_read_polygon_with_holes_geom(t) {
+        t.plan(12);
+        
+        var polygon_with_holes = shell_start + geoms['polygon_with_holes'] + shell_end;
+        var parser = new OpenLayers.Format.GML();
+        data = parser.read(polygon_with_holes);
+        t.eq(data[0].geometry.CLASS_NAME, "OpenLayers.Geometry.Polygon", "Polygon GML returns correct classname");
+        t.eq(data[0].geometry.components[0].components[0].x, 1, "first x coord correct");
+        t.eq(data[0].geometry.components[0].components[0].y, 2, "first y coord correct");
+        t.eq(data[0].geometry.components[0].components[1].x, 4, "second x coord correct");
+        t.eq(data[0].geometry.components[0].components[1].y, 5, "second y coord correct");
+        t.eq(data[0].geometry.components[0].components.length, 4, "coords length correct");
+        t.eq(data[0].geometry.components[1].components[0].x, 11, "first x coord correct");
+        t.eq(data[0].geometry.components[1].components[0].y, 12, "first y coord correct");
+        t.eq(data[0].geometry.components[1].components[1].x, 14, "second x coord correct");
+        t.eq(data[0].geometry.components[1].components[1].y, 15, "second y coord correct");
+        t.eq(data[0].geometry.components[1].components.length, 4, "coords length correct");
+        t.eq(data[0].geometry.components.length, 2, "rings length correct");
+    }
+    function test_Format_GML_read_attributes(t) {
+        t.plan(1);
+        var parser = new OpenLayers.Format.GML();
+        data = parser.read(test_content[0]);
+        t.eq(data[0].attributes['NAME'], "WY", "Simple Attribute data is read correctly.");
+    }    
+
+    var test_content = ['<?xml version="1.0" encoding="utf-8" ?>\n<ogr:FeatureCollection\n     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n     xsi:schemaLocation="http://ogr.maptools.org/ testoutput.xsd"\n     xmlns:ogr="http://ogr.maptools.org/"\n     xmlns:gml="http://www.opengis.net/gml">\n  <gml:boundedBy>\n    <gml:Box>\n      <gml:coord><gml:X>-1254041.389711702</gml:X><gml:Y>250906.9515983529</gml:Y></gml:coord>\n      <gml:coord><gml:X>-634517.1199908922</gml:X><gml:Y>762236.2940800377</gml:Y></gml:coord>\n    </gml:Box>\n  </gml:boundedBy>                    \n  <gml:featureMember>\n    <ogr:states fid="F0">\n      <ogr:geometryProperty><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-634517.11999089224,691849.77929356066,0 -653761.64509297756,471181.53429472551,0 -673343.60852865304,250906.9515983529,0 -1088825.734430399,299284.85108220269,0 -1254041.3897117018,324729.27754874947,0 -1235750.4212498858,434167.33911316615,0 -1190777.7803201093,704392.96327195223,0 -1181607.835811228,762236.29408003774,0 -634517.11999089224,691849.77929356066,0</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>\n      <ogr:NAME>WY</ogr:NAME>\n    </ogr:states>\n  </gml:featureMember>\n</ogr:FeatureCollection>\n',
+ '<wfs:FeatureCollection' + 
  '   xmlns:fs="http://example.com/featureserver"' + 
  '   xmlns:wfs="http://www.opengis.net/wfs"' + 
  '   xmlns:gml="http://www.opengis.net/gml"' + 
@@ -28,37 +179,27 @@
  '</gml:featureMember> ' +
  ' <gml:featureMember><fs:scribble fid="8">    <fs:geometry>        <gml:Point><gml:coordinates>-81.38671875,27.0703125</gml:coordinates></gml:Point>         </fs:geometry> ' +
  '        <fs:down>south</fs:down><fs:title>Florida</fs:title>        </fs:scribble></gml:featureMember>' + 
- '</wfs:FeatureCollection>'; 
-
-    function test_Format_GML_constructor(t) { 
-        t.plan(4); 
-         
-        var options = {'foo': 'bar'}; 
-        var format = new OpenLayers.Format.GML(options); 
-        t.ok(format instanceof OpenLayers.Format.GML, 
-             "new OpenLayers.Format.GML returns object" ); 
-        t.eq(format.foo, "bar", "constructor sets options correctly"); 
-        t.eq(typeof format.read, "function", "format has a read function"); 
-        t.eq(typeof format.write, "function", "format has a write function"); 
-    }
-
-    function test_Format_GML_getFid(t) { 
-        t.plan(2);
-
-        var parser = new OpenLayers.Format.GML();
-        data = parser.read(test_content);
-        t.eq(data[0].fid, '221', 'fid on polygons set correctly (with whitespace)');
-        t.eq(data[1].fid, '8', 'fid on linestrings set correctly with whitespace'); 
-    }
-
-    function test_Format_GML_no_clobber(t) { 
-        t.plan(1); 
-        var parser = new OpenLayers.Format.GML(); 
-        data = parser.read(test_content); 
-        t.eq(window.i, undefined,
-             "i is undefined in window scope after reading."); 
-    }
-
+ '</wfs:FeatureCollection>' 
+ ];
+   
+    var shell_start = '<wfs:FeatureCollection xmlns:wfs="http://www.opengis.net/wfs"><gml:featureMember xmlns:gml="http://www.opengis.net/gml"><feature:features xmlns:feature="http://mapserver.gis.umn.edu/mapserver" fid="221"><feature:geometry>'; 
+    var shell_end = '</feature:geometry></feature:features></gml:featureMember></wfs:FeatureCollection>';
+    var serialize_geoms = {
+        'point': '<gml:Point><gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates></gml:Point>',
+        'linestring': '<gml:LineString><gml:coordinates decimal="." cs="," ts=" ">1,2 4,5</gml:coordinates></gml:LineString>',
+        'polygon': '<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates decimal="." cs="," ts=" ">1,2 4,5 3,6 1,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon>',
+        'multipoint': '<gml:MultiPoint><gml:pointMember><gml:Point><gml:coordinates decimal="." cs="," ts=" ">1,2</gml:coordinates></gml:Point></gml:pointMember><gml:pointMember><gml:Point><gml:coordinates decimal="." cs="," ts=" ">4,5</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint>',
+        'multilinestring': '<gml:MultiLineString><gml:lineStringMember><gml:LineString><gml:coordinates decimal="." cs="," ts=" ">1,2 4,5</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates decimal="." cs="," ts=" ">11,12 14,15</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString>'
+    };
+    var geoms = {
+        'point': '<gml:Point><gml:coordinates>1,2,3</gml:coordinates></gml:Point>',
+        'linestring': '<gml:LineString><gml:coordinates>1,2,3 4,5,6</gml:coordinates></gml:LineString>',
+        'polygon': '<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,2 4,5 3,6 1,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon>',
+        'polygon_with_holes': '<gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,2 4,5 3,6 1,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>11,12 14,15 13,16 11,12</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon>',
+        'multipoint': '<gml:MultiPoint><gml:Point><gml:coordinates>1,2,3</gml:coordinates></gml:Point><gml:Point><gml:coordinates>4,5,6</gml:coordinates></gml:Point></gml:MultiPoint>',
+        'multilinestring': '<gml:MultiLineString><gml:LineString><gml:coordinates>1,2,3 4,5,6</gml:coordinates></gml:LineString><gml:LineString><gml:coordinates>11,12,13 14,15,16</gml:coordinates></gml:LineString></gml:MultiLineString>' // ,
+        // 'multipolygon_with_holes': '<gml:MultiPolygon><gml:Polygon><gml:outerBoundaryIs>1,2 4,5 3,6 1,2</gml:outerBoundaryIs><gml:innerBoundaryIs>11,12 14,15 13,16 11,12</gml:innerBoundaryIs></gml:Polygon><gml:Polygon><gml:outerBoundaryIs>101,102 104,105 103,106 101,102</gml:outerBoundaryIs><gml:innerBoundaryIs>111,112 114,115 113,116 111,112</gml:innerBoundaryIs></gml:Polygon></gml:MultiPolygon>'
+    };
     </script> 
 </head> 
 <body> 



More information about the Commits mailing list