]> dev.renevier.net Git - syp.git/blob - openlayers/lib/OpenLayers/Format/GML.js
initial commit
[syp.git] / openlayers / lib / OpenLayers / Format / GML.js
1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2  * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3  * full text of the license. */
4
5 /**
6  * @requires OpenLayers/Format/XML.js
7  * @requires OpenLayers/Feature/Vector.js
8  * @requires OpenLayers/Geometry/Point.js
9  * @requires OpenLayers/Geometry/MultiPoint.js
10  * @requires OpenLayers/Geometry/LineString.js
11  * @requires OpenLayers/Geometry/MultiLineString.js
12  * @requires OpenLayers/Geometry/Polygon.js
13  * @requires OpenLayers/Geometry/MultiPolygon.js
14  * @requires OpenLayers/Console.js
15  */
16
17 /**
18  * Class: OpenLayers.Format.GML
19  * Read/Wite GML. Create a new instance with the <OpenLayers.Format.GML>
20  *     constructor.  Supports the GML simple features profile.
21  * 
22  * Inherits from:
23  *  - <OpenLayers.Format>
24  */
25 OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
26     
27     /*
28      * APIProperty: featureNS
29      * {String} Namespace used for feature attributes.  Default is
30      *     "http://mapserver.gis.umn.edu/mapserver".
31      */
32     featureNS: "http://mapserver.gis.umn.edu/mapserver",
33     
34     /**
35      * APIProperty: featurePrefix
36      * {String} Namespace alias (or prefix) for feature nodes.  Default is
37      *     "feature".
38      */
39     featurePrefix: "feature",
40     
41     /*
42      * APIProperty: featureName
43      * {String} Element name for features. Default is "featureMember".
44      */
45     featureName: "featureMember", 
46     
47     /*
48      * APIProperty: layerName
49      * {String} Name of data layer. Default is "features".
50      */
51     layerName: "features",
52     
53     /**
54      * APIProperty: geometryName
55      * {String} Name of geometry element.  Defaults to "geometry".
56      */
57     geometryName: "geometry",
58     
59     /** 
60      * APIProperty: collectionName
61      * {String} Name of featureCollection element.
62      */
63     collectionName: "FeatureCollection",
64     
65     /**
66      * APIProperty: gmlns
67      * {String} GML Namespace.
68      */
69     gmlns: "http://www.opengis.net/gml",
70
71     /**
72      * APIProperty: extractAttributes
73      * {Boolean} Extract attributes from GML.
74      */
75     extractAttributes: true,
76     
77     /**
78      * APIProperty: xy
79      * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
80      * Changing is not recommended, a new Format should be instantiated.
81      */ 
82     xy: true,
83     
84     /**
85      * Constructor: OpenLayers.Format.GML
86      * Create a new parser for GML.
87      *
88      * Parameters:
89      * options - {Object} An optional object whose properties will be set on
90      *     this instance.
91      */
92     initialize: function(options) {
93         // compile regular expressions once instead of every time they are used
94         this.regExes = {
95             trimSpace: (/^\s*|\s*$/g),
96             removeSpace: (/\s*/g),
97             splitSpace: (/\s+/),
98             trimComma: (/\s*,\s*/g)
99         };
100         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
101     },
102
103     /**
104      * APIMethod: read
105      * Read data from a string, and return a list of features. 
106      * 
107      * Parameters:
108      * data - {String} or {DOMElement} data to read/parse.
109      *
110      * Returns:
111      * {Array(<OpenLayers.Feature.Vector>)} An array of features.
112      */
113     read: function(data) {
114         if(typeof data == "string") { 
115             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
116         }
117         var featureNodes = this.getElementsByTagNameNS(data.documentElement,
118                                                        this.gmlns,
119                                                        this.featureName);
120         var features = [];
121         for(var i=0; i<featureNodes.length; i++) {
122             var feature = this.parseFeature(featureNodes[i]);
123             if(feature) {
124                 features.push(feature);
125             }
126         }
127         return features;
128     },
129     
130     /**
131      * Method: parseFeature
132      * This function is the core of the GML parsing code in OpenLayers.
133      *    It creates the geometries that are then attached to the returned
134      *    feature, and calls parseAttributes() to get attribute data out.
135      *    
136      * Parameters:
137      * node - {DOMElement} A GML feature node. 
138      */
139     parseFeature: function(node) {
140         // only accept one geometry per feature - look for highest "order"
141         var order = ["MultiPolygon", "Polygon",
142                      "MultiLineString", "LineString",
143                      "MultiPoint", "Point", "Envelope", "Box"];
144         var type, nodeList, geometry, parser;
145         for(var i=0; i<order.length; ++i) {
146             type = order[i];
147             nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
148             if(nodeList.length > 0) {
149                 // only deal with first geometry of this type
150                 var parser = this.parseGeometry[type.toLowerCase()];
151                 if(parser) {
152                     geometry = parser.apply(this, [nodeList[0]]);
153                     if (this.internalProjection && this.externalProjection) {
154                         geometry.transform(this.externalProjection, 
155                                            this.internalProjection); 
156                     }                       
157                 } else {
158                     OpenLayers.Console.error(OpenLayers.i18n(
159                                 "unsupportedGeometryType", {'geomType':type}));
160                 }
161                 // stop looking for different geometry types
162                 break;
163             }
164         }
165         
166         // construct feature (optionally with attributes)
167         var attributes;
168         if(this.extractAttributes) {
169             attributes = this.parseAttributes(node);
170         }
171         var feature = new OpenLayers.Feature.Vector(geometry, attributes);
172         
173         feature.gml = {
174             featureType: node.firstChild.nodeName.split(":")[1],
175             featureNS: node.firstChild.namespaceURI,
176             featureNSPrefix: node.firstChild.prefix
177         };
178                 
179         // assign fid - this can come from a "fid" or "id" attribute
180         var childNode = node.firstChild;
181         var fid;
182         while(childNode) {
183             if(childNode.nodeType == 1) {
184                 fid = childNode.getAttribute("fid") ||
185                       childNode.getAttribute("id");
186                 if(fid) {
187                     break;
188                 }
189             }
190             childNode = childNode.nextSibling;
191         }
192         feature.fid = fid;
193         return feature;
194     },
195     
196     /**
197      * Property: parseGeometry
198      * Properties of this object are the functions that parse geometries based
199      *     on their type.
200      */
201     parseGeometry: {
202         
203         /**
204          * Method: parseGeometry.point
205          * Given a GML node representing a point geometry, create an OpenLayers
206          *     point geometry.
207          *
208          * Parameters:
209          * node - {DOMElement} A GML node.
210          *
211          * Returns:
212          * {<OpenLayers.Geometry.Point>} A point geometry.
213          */
214         point: function(node) {
215             /**
216              * Three coordinate variations to consider:
217              * 1) <gml:pos>x y z</gml:pos>
218              * 2) <gml:coordinates>x, y, z</gml:coordinates>
219              * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
220              */
221             var nodeList, coordString;
222             var coords = [];
223
224             // look for <gml:pos>
225             var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
226             if(nodeList.length > 0) {
227                 coordString = nodeList[0].firstChild.nodeValue;
228                 coordString = coordString.replace(this.regExes.trimSpace, "");
229                 coords = coordString.split(this.regExes.splitSpace);
230             }
231
232             // look for <gml:coordinates>
233             if(coords.length == 0) {
234                 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
235                                                        "coordinates");
236                 if(nodeList.length > 0) {
237                     coordString = nodeList[0].firstChild.nodeValue;
238                     coordString = coordString.replace(this.regExes.removeSpace,
239                                                       "");
240                     coords = coordString.split(",");
241                 }
242             }
243
244             // look for <gml:coord>
245             if(coords.length == 0) {
246                 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
247                                                        "coord");
248                 if(nodeList.length > 0) {
249                     var xList = this.getElementsByTagNameNS(nodeList[0],
250                                                             this.gmlns, "X");
251                     var yList = this.getElementsByTagNameNS(nodeList[0],
252                                                             this.gmlns, "Y");
253                     if(xList.length > 0 && yList.length > 0) {
254                         coords = [xList[0].firstChild.nodeValue,
255                                   yList[0].firstChild.nodeValue];
256                     }
257                 }
258             }
259                 
260             // preserve third dimension
261             if(coords.length == 2) {
262                 coords[2] = null;
263             }
264             
265             if (this.xy) {
266                 return new OpenLayers.Geometry.Point(coords[0], coords[1],
267                                                  coords[2]);
268             }
269             else{
270                 return new OpenLayers.Geometry.Point(coords[1], coords[0],
271                                                  coords[2]);
272             }
273         },
274         
275         /**
276          * Method: parseGeometry.multipoint
277          * Given a GML node representing a multipoint geometry, create an
278          *     OpenLayers multipoint geometry.
279          *
280          * Parameters:
281          * node - {DOMElement} A GML node.
282          *
283          * Returns:
284          * {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
285          */
286         multipoint: function(node) {
287             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
288                                                        "Point");
289             var components = [];
290             if(nodeList.length > 0) {
291                 var point;
292                 for(var i=0; i<nodeList.length; ++i) {
293                     point = this.parseGeometry.point.apply(this, [nodeList[i]]);
294                     if(point) {
295                         components.push(point);
296                     }
297                 }
298             }
299             return new OpenLayers.Geometry.MultiPoint(components);
300         },
301         
302         /**
303          * Method: parseGeometry.linestring
304          * Given a GML node representing a linestring geometry, create an
305          *     OpenLayers linestring geometry.
306          *
307          * Parameters:
308          * node - {DOMElement} A GML node.
309          *
310          * Returns:
311          * {<OpenLayers.Geometry.LineString>} A linestring geometry.
312          */
313         linestring: function(node, ring) {
314             /**
315              * Two coordinate variations to consider:
316              * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
317              * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
318              */
319             var nodeList, coordString;
320             var coords = [];
321             var points = [];
322
323             // look for <gml:posList>
324             nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
325             if(nodeList.length > 0) {
326                 coordString = this.getChildValue(nodeList[0]);
327                 coordString = coordString.replace(this.regExes.trimSpace, "");
328                 coords = coordString.split(this.regExes.splitSpace);
329                 var dim = parseInt(nodeList[0].getAttribute("dimension"));
330                 var j, x, y, z;
331                 for(var i=0; i<coords.length/dim; ++i) {
332                     j = i * dim;
333                     x = coords[j];
334                     y = coords[j+1];
335                     z = (dim == 2) ? null : coords[j+2];
336                     if (this.xy) {
337                         points.push(new OpenLayers.Geometry.Point(x, y, z));
338                     } else {
339                         points.push(new OpenLayers.Geometry.Point(y, x, z));
340                     }
341                 }
342             }
343
344             // look for <gml:coordinates>
345             if(coords.length == 0) {
346                 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
347                                                        "coordinates");
348                 if(nodeList.length > 0) {
349                     coordString = this.getChildValue(nodeList[0]);
350                     coordString = coordString.replace(this.regExes.trimSpace,
351                                                       "");
352                     coordString = coordString.replace(this.regExes.trimComma,
353                                                       ",");
354                     var pointList = coordString.split(this.regExes.splitSpace);
355                     for(var i=0; i<pointList.length; ++i) {
356                         coords = pointList[i].split(",");
357                         if(coords.length == 2) {
358                             coords[2] = null;
359                         }
360                         if (this.xy) {
361                             points.push(new OpenLayers.Geometry.Point(coords[0],
362                                                                   coords[1],
363                                                                   coords[2]));
364                         } else {
365                             points.push(new OpenLayers.Geometry.Point(coords[1],
366                                                                   coords[0],
367                                                                   coords[2]));
368                         }
369                     }
370                 }
371             }
372
373             var line = null;
374             if(points.length != 0) {
375                 if(ring) {
376                     line = new OpenLayers.Geometry.LinearRing(points);
377                 } else {
378                     line = new OpenLayers.Geometry.LineString(points);
379                 }
380             }
381             return line;
382         },
383         
384         /**
385          * Method: parseGeometry.multilinestring
386          * Given a GML node representing a multilinestring geometry, create an
387          *     OpenLayers multilinestring geometry.
388          *
389          * Parameters:
390          * node - {DOMElement} A GML node.
391          *
392          * Returns:
393          * {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
394          */
395         multilinestring: function(node) {
396             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
397                                                        "LineString");
398             var components = [];
399             if(nodeList.length > 0) {
400                 var line;
401                 for(var i=0; i<nodeList.length; ++i) {
402                     line = this.parseGeometry.linestring.apply(this,
403                                                                [nodeList[i]]);
404                     if(line) {
405                         components.push(line);
406                     }
407                 }
408             }
409             return new OpenLayers.Geometry.MultiLineString(components);
410         },
411         
412         /**
413          * Method: parseGeometry.polygon
414          * Given a GML node representing a polygon geometry, create an
415          *     OpenLayers polygon geometry.
416          *
417          * Parameters:
418          * node - {DOMElement} A GML node.
419          *
420          * Returns:
421          * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
422          */
423         polygon: function(node) {
424             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
425                                                        "LinearRing");
426             var components = [];
427             if(nodeList.length > 0) {
428                 // this assumes exterior ring first, inner rings after
429                 var ring;
430                 for(var i=0; i<nodeList.length; ++i) {
431                     ring = this.parseGeometry.linestring.apply(this,
432                                                         [nodeList[i], true]);
433                     if(ring) {
434                         components.push(ring);
435                     }
436                 }
437             }
438             return new OpenLayers.Geometry.Polygon(components);
439         },
440         
441         /**
442          * Method: parseGeometry.multipolygon
443          * Given a GML node representing a multipolygon geometry, create an
444          *     OpenLayers multipolygon geometry.
445          *
446          * Parameters:
447          * node - {DOMElement} A GML node.
448          *
449          * Returns:
450          * {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry.
451          */
452         multipolygon: function(node) {
453             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
454                                                        "Polygon");
455             var components = [];
456             if(nodeList.length > 0) {
457                 var polygon;
458                 for(var i=0; i<nodeList.length; ++i) {
459                     polygon = this.parseGeometry.polygon.apply(this,
460                                                                [nodeList[i]]);
461                     if(polygon) {
462                         components.push(polygon);
463                     }
464                 }
465             }
466             return new OpenLayers.Geometry.MultiPolygon(components);
467         },
468         
469         envelope: function(node) {
470             var components = [];
471             var coordString;
472             var envelope;
473             
474             var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner");
475             if (lpoint.length > 0) {
476                 var coords = [];
477                 
478                 if(lpoint.length > 0) {
479                     coordString = lpoint[0].firstChild.nodeValue;
480                     coordString = coordString.replace(this.regExes.trimSpace, "");
481                     coords = coordString.split(this.regExes.splitSpace);
482                 }
483                 
484                 if(coords.length == 2) {
485                     coords[2] = null;
486                 }
487                 if (this.xy) {
488                     var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
489                 } else {
490                     var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
491                 }
492             }
493             
494             var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner");
495             if (upoint.length > 0) {
496                 var coords = [];
497                 
498                 if(upoint.length > 0) {
499                     coordString = upoint[0].firstChild.nodeValue;
500                     coordString = coordString.replace(this.regExes.trimSpace, "");
501                     coords = coordString.split(this.regExes.splitSpace);
502                 }
503                 
504                 if(coords.length == 2) {
505                     coords[2] = null;
506                 }
507                 if (this.xy) {
508                     var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
509                 } else {
510                     var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
511                 }
512             }
513             
514             if (lowerPoint && upperPoint) {
515                 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
516                 components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));
517                 components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));
518                 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));
519                 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
520                 
521                 var ring = new OpenLayers.Geometry.LinearRing(components);
522                 envelope = new OpenLayers.Geometry.Polygon([ring]);
523             }
524             return envelope; 
525         }
526     },
527     
528     /**
529      * Method: parseAttributes
530      *
531      * Parameters:
532      * node - {<DOMElement>}
533      *
534      * Returns:
535      * {Object} An attributes object.
536      */
537     parseAttributes: function(node) {
538         var attributes = {};
539         // assume attributes are children of the first type 1 child
540         var childNode = node.firstChild;
541         var children, i, child, grandchildren, grandchild, name, value;
542         while(childNode) {
543             if(childNode.nodeType == 1) {
544                 // attributes are type 1 children with one type 3 child
545                 children = childNode.childNodes;
546                 for(i=0; i<children.length; ++i) {
547                     child = children[i];
548                     if(child.nodeType == 1) {
549                         grandchildren = child.childNodes;
550                         if(grandchildren.length == 1) {
551                             grandchild = grandchildren[0];
552                             if(grandchild.nodeType == 3 ||
553                                grandchild.nodeType == 4) {
554                                 name = (child.prefix) ?
555                                         child.nodeName.split(":")[1] :
556                                         child.nodeName;
557                                 value = grandchild.nodeValue.replace(
558                                                 this.regExes.trimSpace, "");
559                                 attributes[name] = value;
560                             }
561                         } else {
562                             // If child has no childNodes (grandchildren),
563                             // set an attribute with null value.
564                             // e.g. <prefix:fieldname/> becomes
565                             // {fieldname: null}
566                             attributes[child.nodeName.split(":").pop()] = null;
567                         }
568                     }
569                 }
570                 break;
571             }
572             childNode = childNode.nextSibling;
573         }
574         return attributes;
575     },
576     
577     /**
578      * APIMethod: write
579      * Generate a GML document string given a list of features. 
580      * 
581      * Parameters:
582      * features - {Array(<OpenLayers.Feature.Vector>)} List of features to
583      *     serialize into a string.
584      *
585      * Returns:
586      * {String} A string representing the GML document.
587      */
588     write: function(features) {
589         if(!(features instanceof Array)) {
590             features = [features];
591         }
592         var gml = this.createElementNS("http://www.opengis.net/wfs",
593                                        "wfs:" + this.collectionName);
594         for(var i=0; i<features.length; i++) {
595             gml.appendChild(this.createFeatureXML(features[i]));
596         }
597         return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
598     },
599
600     /** 
601      * Method: createFeatureXML
602      * Accept an OpenLayers.Feature.Vector, and build a GML node for it.
603      *
604      * Parameters:
605      * feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML.
606      *
607      * Returns:
608      * {DOMElement} A node reprensting the feature in GML.
609      */
610     createFeatureXML: function(feature) {
611         var geometry = feature.geometry;
612         var geometryNode = this.buildGeometryNode(geometry);
613         var geomContainer = this.createElementNS(this.featureNS,
614                                                  this.featurePrefix + ":" +
615                                                  this.geometryName);
616         geomContainer.appendChild(geometryNode);
617         var featureNode = this.createElementNS(this.gmlns,
618                                                "gml:" + this.featureName);
619         var featureContainer = this.createElementNS(this.featureNS,
620                                                     this.featurePrefix + ":" +
621                                                     this.layerName);
622         var fid = feature.fid || feature.id;
623         featureContainer.setAttribute("fid", fid);
624         featureContainer.appendChild(geomContainer);
625         for(var attr in feature.attributes) {
626             var attrText = this.createTextNode(feature.attributes[attr]); 
627             var nodename = attr.substring(attr.lastIndexOf(":") + 1);
628             var attrContainer = this.createElementNS(this.featureNS,
629                                                      this.featurePrefix + ":" +
630                                                      nodename);
631             attrContainer.appendChild(attrText);
632             featureContainer.appendChild(attrContainer);
633         }    
634         featureNode.appendChild(featureContainer);
635         return featureNode;
636     },
637     
638     /**
639      * APIMethod: buildGeometryNode
640      */
641     buildGeometryNode: function(geometry) {
642         if (this.externalProjection && this.internalProjection) {
643             geometry = geometry.clone();
644             geometry.transform(this.internalProjection, 
645                                this.externalProjection);
646         }    
647         var className = geometry.CLASS_NAME;
648         var type = className.substring(className.lastIndexOf(".") + 1);
649         var builder = this.buildGeometry[type.toLowerCase()];
650         return builder.apply(this, [geometry]);
651     },
652
653     /**
654      * Property: buildGeometry
655      * Object containing methods to do the actual geometry node building
656      *     based on geometry type.
657      */
658     buildGeometry: {
659         // TBD retrieve the srs from layer
660         // srsName is non-standard, so not including it until it's right.
661         // gml.setAttribute("srsName",
662         //                  "http://www.opengis.net/gml/srs/epsg.xml#4326");
663
664         /**
665          * Method: buildGeometry.point
666          * Given an OpenLayers point geometry, create a GML point.
667          *
668          * Parameters:
669          * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
670          *
671          * Returns:
672          * {DOMElement} A GML point node.
673          */
674         point: function(geometry) {
675             var gml = this.createElementNS(this.gmlns, "gml:Point");
676             gml.appendChild(this.buildCoordinatesNode(geometry));
677             return gml;
678         },
679         
680         /**
681          * Method: buildGeometry.multipoint
682          * Given an OpenLayers multipoint geometry, create a GML multipoint.
683          *
684          * Parameters:
685          * geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
686          *
687          * Returns:
688          * {DOMElement} A GML multipoint node.
689          */
690         multipoint: function(geometry) {
691             var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
692             var points = geometry.components;
693             var pointMember, pointGeom;
694             for(var i=0; i<points.length; i++) { 
695                 pointMember = this.createElementNS(this.gmlns,
696                                                    "gml:pointMember");
697                 pointGeom = this.buildGeometry.point.apply(this,
698                                                                [points[i]]);
699                 pointMember.appendChild(pointGeom);
700                 gml.appendChild(pointMember);
701             }
702             return gml;            
703         },
704         
705         /**
706          * Method: buildGeometry.linestring
707          * Given an OpenLayers linestring geometry, create a GML linestring.
708          *
709          * Parameters:
710          * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
711          *
712          * Returns:
713          * {DOMElement} A GML linestring node.
714          */
715         linestring: function(geometry) {
716             var gml = this.createElementNS(this.gmlns, "gml:LineString");
717             gml.appendChild(this.buildCoordinatesNode(geometry));
718             return gml;
719         },
720         
721         /**
722          * Method: buildGeometry.multilinestring
723          * Given an OpenLayers multilinestring geometry, create a GML
724          *     multilinestring.
725          *
726          * Parameters:
727          * geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring
728          *     geometry.
729          *
730          * Returns:
731          * {DOMElement} A GML multilinestring node.
732          */
733         multilinestring: function(geometry) {
734             var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
735             var lines = geometry.components;
736             var lineMember, lineGeom;
737             for(var i=0; i<lines.length; ++i) {
738                 lineMember = this.createElementNS(this.gmlns,
739                                                   "gml:lineStringMember");
740                 lineGeom = this.buildGeometry.linestring.apply(this,
741                                                                    [lines[i]]);
742                 lineMember.appendChild(lineGeom);
743                 gml.appendChild(lineMember);
744             }
745             return gml;
746         },
747         
748         /**
749          * Method: buildGeometry.linearring
750          * Given an OpenLayers linearring geometry, create a GML linearring.
751          *
752          * Parameters:
753          * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
754          *
755          * Returns:
756          * {DOMElement} A GML linearring node.
757          */
758         linearring: function(geometry) {
759             var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
760             gml.appendChild(this.buildCoordinatesNode(geometry));
761             return gml;
762         },
763         
764         /**
765          * Method: buildGeometry.polygon
766          * Given an OpenLayers polygon geometry, create a GML polygon.
767          *
768          * Parameters:
769          * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
770          *
771          * Returns:
772          * {DOMElement} A GML polygon node.
773          */
774         polygon: function(geometry) {
775             var gml = this.createElementNS(this.gmlns, "gml:Polygon");
776             var rings = geometry.components;
777             var ringMember, ringGeom, type;
778             for(var i=0; i<rings.length; ++i) {
779                 type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
780                 ringMember = this.createElementNS(this.gmlns,
781                                                   "gml:" + type);
782                 ringGeom = this.buildGeometry.linearring.apply(this,
783                                                                    [rings[i]]);
784                 ringMember.appendChild(ringGeom);
785                 gml.appendChild(ringMember);
786             }
787             return gml;
788         },
789         
790         /**
791          * Method: buildGeometry.multipolygon
792          * Given an OpenLayers multipolygon geometry, create a GML multipolygon.
793          *
794          * Parameters:
795          * geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon
796          *     geometry.
797          *
798          * Returns:
799          * {DOMElement} A GML multipolygon node.
800          */
801         multipolygon: function(geometry) {
802             var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
803             var polys = geometry.components;
804             var polyMember, polyGeom;
805             for(var i=0; i<polys.length; ++i) {
806                 polyMember = this.createElementNS(this.gmlns,
807                                                   "gml:polygonMember");
808                 polyGeom = this.buildGeometry.polygon.apply(this,
809                                                                 [polys[i]]);
810                 polyMember.appendChild(polyGeom);
811                 gml.appendChild(polyMember);
812             }
813             return gml;
814
815         },
816  
817         /**
818          * Method: buildGeometry.bounds
819          * Given an OpenLayers bounds, create a GML box.
820          *
821          * Parameters:
822          * bounds - {<OpenLayers.Geometry.Bounds>} A bounds object.
823          *
824          * Returns:
825          * {DOMElement} A GML box node.
826          */
827         bounds: function(bounds) {
828             var gml = this.createElementNS(this.gmlns, "gml:Box");
829             gml.appendChild(this.buildCoordinatesNode(bounds));
830             return gml;
831         }
832     },
833
834     /**
835      * Method: buildCoordinates
836      * builds the coordinates XmlNode
837      * (code)
838      * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
839      * (end)
840      * Parameters: 
841      * geometry - {<OpenLayers.Geometry>} 
842      *
843      * Returns:
844      * {XmlNode} created xmlNode
845      */
846     buildCoordinatesNode: function(geometry) {
847         var coordinatesNode = this.createElementNS(this.gmlns,
848                                                    "gml:coordinates");
849         coordinatesNode.setAttribute("decimal", ".");
850         coordinatesNode.setAttribute("cs", ",");
851         coordinatesNode.setAttribute("ts", " ");
852
853         var parts = [];
854
855         if(geometry instanceof OpenLayers.Bounds){
856             parts.push(geometry.left + "," + geometry.bottom);
857             parts.push(geometry.right + "," + geometry.top);
858         } else {
859             var points = (geometry.components) ? geometry.components : [geometry];
860             for(var i=0; i<points.length; i++) {
861                 parts.push(points[i].x + "," + points[i].y);                
862             }            
863         }
864
865         var txtNode = this.createTextNode(parts.join(" "));
866         coordinatesNode.appendChild(txtNode);
867         
868         return coordinatesNode;
869     },
870
871     CLASS_NAME: "OpenLayers.Format.GML" 
872 });