]> dev.renevier.net Git - syp.git/blob - openlayers/lib/OpenLayers/Format/GML/Base.js
initial commit
[syp.git] / openlayers / lib / OpenLayers / Format / GML / Base.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/Format/GML.js
8  */
9
10 /**
11  * Though required in the full build, if the GML format is excluded, we set
12  * the namespace here.
13  */
14 if(!OpenLayers.Format.GML) {
15     OpenLayers.Format.GML = {};
16 }
17
18 /**
19  * Class: OpenLayers.Format.GML.Base
20  * Superclass for GML parsers.
21  *
22  * Inherits from:
23  *  - <OpenLayers.Format.XML>
24  */
25 OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
26     
27     /**
28      * Property: namespaces
29      * {Object} Mapping of namespace aliases to namespace URIs.
30      */
31     namespaces: {
32         gml: "http://www.opengis.net/gml",
33         xlink: "http://www.w3.org/1999/xlink",
34         xsi: "http://www.w3.org/2001/XMLSchema-instance",
35         wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
36     },
37     
38     /**
39      * Property: defaultPrefix
40      */
41     defaultPrefix: "gml",
42
43     /**
44      * Property: schemaLocation
45      * {String} Schema location for a particular minor version.
46      */
47     schemaLocation: null,
48     
49     /**
50      * APIProperty: featureType
51      * {Array(String) or String} The local (without prefix) feature typeName(s).
52      */
53     featureType: null,
54     
55     /**
56      * APIProperty: featureNS
57      * {String} The feature namespace.  Must be set in the options at
58      *     construction.
59      */
60     featureNS: null,
61
62     /**
63      * APIProperty: geometry
64      * {String} Name of geometry element.  Defaults to "geometry".
65      */
66     geometryName: "geometry",
67
68     /**
69      * APIProperty: extractAttributes
70      * {Boolean} Extract attributes from GML.  Default is true.
71      */
72     extractAttributes: true,
73     
74     /**
75      * APIProperty: srsName
76      * {String} URI for spatial reference system.  This is optional for
77      *     single part geometries and mandatory for collections and multis.
78      *     If set, the srsName attribute will be written for all geometries.
79      *     Default is null.
80      */
81     srsName: null,
82
83     /**
84      * APIProperty: xy
85      * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
86      * Changing is not recommended, a new Format should be instantiated.
87      */ 
88     xy: true,
89
90     /**
91      * Property: geometryTypes
92      * {Object} Maps OpenLayers geometry class names to GML element names.
93      *     Use <setGeometryTypes> before accessing this property.
94      */
95     geometryTypes: null,
96
97     /**
98      * Property: singleFeatureType
99      * {Boolean} True if there is only 1 featureType, and not an array
100      *     of featuretypes.
101      */
102     singleFeatureType: null,
103
104     /**
105      * Property: regExes
106      * Compiled regular expressions for manipulating strings.
107      */
108     regExes: {
109         trimSpace: (/^\s*|\s*$/g),
110         removeSpace: (/\s*/g),
111         splitSpace: (/\s+/),
112         trimComma: (/\s*,\s*/g)
113     },
114
115     /**
116      * Constructor: OpenLayers.Format.GML.Base
117      * Instances of this class are not created directly.  Use the
118      *     <OpenLayers.Format.GML.v2> or <OpenLayers.Format.GML.v3> constructor
119      *     instead.
120      *
121      * Parameters:
122      * options - {Object} An optional object whose properties will be set on
123      *     this instance.
124      *
125      * Valid options properties:
126      * featureType - {Array(String) or String} Local (without prefix) feature 
127      *     typeName(s) (required).
128      * featureNS - {String} Feature namespace (required).
129      * geometryName - {String} Geometry element name.
130      */
131     initialize: function(options) {
132         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
133         this.setGeometryTypes();
134         if(options && options.featureNS) {
135             this.setNamespace("feature", options.featureNS);
136         }
137         this.singleFeatureType = !options || (typeof options.featureType === "string");
138     },
139     
140     /**
141      * Method: read
142      *
143      * Parameters:
144      * data - {DOMElement} A gml:featureMember element, a gml:featureMembers
145      *     element, or an element containing either of the above at any level.
146      *
147      * Returns:
148      * {Array(<OpenLayers.Feature.Vector>)} An array of features.
149      */
150     read: function(data) {
151         if(typeof data == "string") { 
152             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
153         }
154         if(data && data.nodeType == 9) {
155             data = data.documentElement;
156         }
157         var features = [];
158         this.readNode(data, {features: features});
159         if(features.length == 0) {
160             // look for gml:featureMember elements
161             var elements = this.getElementsByTagNameNS(
162                 data, this.namespaces.gml, "featureMember"
163             );
164             if(elements.length) {
165                 for(var i=0, len=elements.length; i<len; ++i) {
166                     this.readNode(elements[i], {features: features});
167                 }
168             } else {
169                 // look for gml:featureMembers elements (this is v3, but does no harm here)
170                 var elements = this.getElementsByTagNameNS(
171                     data, this.namespaces.gml, "featureMembers"
172                 );
173                 if(elements.length) {
174                     // there can be only one
175                     this.readNode(elements[0], {features: features});
176                 }
177             }
178         }
179         return features;
180     },
181     
182     /**
183      * Property: readers
184      * Contains public functions, grouped by namespace prefix, that will
185      *     be applied when a namespaced node is found matching the function
186      *     name.  The function will be applied in the scope of this parser
187      *     with two arguments: the node being read and a context object passed
188      *     from the parent.
189      */
190     readers: {
191         "gml": {
192             "featureMember": function(node, obj) {
193                 this.readChildNodes(node, obj);
194             },
195             "featureMembers": function(node, obj) {
196                 this.readChildNodes(node, obj);                
197             },
198             "name": function(node, obj) {
199                 obj.name = this.getChildValue(node);
200             },
201             "boundedBy": function(node, obj) {
202                 var container = {};
203                 this.readChildNodes(node, container);
204                 if(container.components && container.components.length > 0) {
205                     obj.bounds = container.components[0];
206                 }
207             },
208             "Point": function(node, container) {
209                 var obj = {points: []};
210                 this.readChildNodes(node, obj);
211                 if(!container.components) {
212                     container.components = [];
213                 }
214                 container.components.push(obj.points[0]);
215             },
216             "coordinates": function(node, obj) {
217                 var str = this.getChildValue(node).replace(
218                     this.regExes.trimSpace, ""
219                 );
220                 str = str.replace(this.regExes.trimComma, ",");
221                 var pointList = str.split(this.regExes.splitSpace);
222                 var coords;
223                 var numPoints = pointList.length;
224                 var points = new Array(numPoints);
225                 for(var i=0; i<numPoints; ++i) {
226                     coords = pointList[i].split(",");
227                     if (this.xy) {
228                         points[i] = new OpenLayers.Geometry.Point(
229                             coords[0], coords[1], coords[2]
230                         );
231                     } else {
232                         points[i] = new OpenLayers.Geometry.Point(
233                             coords[1], coords[0], coords[2]
234                         );
235                     }
236                 }
237                 obj.points = points;
238             },
239             "coord": function(node, obj) {
240                 var coord = {};
241                 this.readChildNodes(node, coord);
242                 if(!obj.points) {
243                     obj.points = [];
244                 }
245                 obj.points.push(new OpenLayers.Geometry.Point(
246                     coord.x, coord.y, coord.z
247                 ));
248             },
249             "X": function(node, coord) {
250                 coord.x = this.getChildValue(node);
251             },
252             "Y": function(node, coord) {
253                 coord.y = this.getChildValue(node);
254             },
255             "Z": function(node, coord) {
256                 coord.z = this.getChildValue(node);
257             },
258             "MultiPoint": function(node, container) {
259                 var obj = {components: []};
260                 this.readChildNodes(node, obj);
261                 container.components = [
262                     new OpenLayers.Geometry.MultiPoint(obj.components)
263                 ];
264             },
265             "pointMember": function(node, obj) {
266                 this.readChildNodes(node, obj);
267             },
268             "LineString": function(node, container) {
269                 var obj = {};
270                 this.readChildNodes(node, obj);
271                 if(!container.components) {
272                     container.components = [];
273                 }
274                 container.components.push(
275                     new OpenLayers.Geometry.LineString(obj.points)
276                 );
277             },
278             "MultiLineString": function(node, container) {
279                 var obj = {components: []};
280                 this.readChildNodes(node, obj);
281                 container.components = [
282                     new OpenLayers.Geometry.MultiLineString(obj.components)
283                 ];
284             },
285             "lineStringMember": function(node, obj) {
286                 this.readChildNodes(node, obj);
287             },
288             "Polygon": function(node, container) {
289                 var obj = {outer: null, inner: []};
290                 this.readChildNodes(node, obj);
291                 obj.inner.unshift(obj.outer);
292                 if(!container.components) {
293                     container.components = [];
294                 }
295                 container.components.push(
296                     new OpenLayers.Geometry.Polygon(obj.inner)
297                 );
298             },
299             "LinearRing": function(node, obj) {
300                 var container = {};
301                 this.readChildNodes(node, container);
302                 obj.components = [new OpenLayers.Geometry.LinearRing(
303                     container.points
304                 )];
305             },
306             "MultiPolygon": function(node, container) {
307                 var obj = {components: []};
308                 this.readChildNodes(node, obj);
309                 container.components = [
310                     new OpenLayers.Geometry.MultiPolygon(obj.components)
311                 ];
312             },
313             "polygonMember": function(node, obj) {
314                 this.readChildNodes(node, obj);
315             },
316             "GeometryCollection": function(node, container) {
317                 var obj = {components: []};
318                 this.readChildNodes(node, obj);
319                 container.components = [
320                     new OpenLayers.Geometry.Collection(obj.components)
321                 ];
322             },
323             "geometryMember": function(node, obj) {
324                 this.readChildNodes(node, obj);
325             }
326         },
327         "feature": {
328             "*": function(node, obj) {
329                 // The node can either be named like the featureType, or it
330                 // can be a child of the feature:featureType.  Children can be
331                 // geometry or attributes.
332                 var name;
333                 var local = node.localName || node.nodeName.split(":").pop();
334                 if (!this.singleFeatureType && 
335                     (OpenLayers.Util.indexOf(this.featureType, local) != -1)) {
336                         name = "_typeName";
337                 }
338                 else if(local == this.featureType) {
339                     name = "_typeName";
340                 } else {
341                     // Assume attribute elements have one child node and that the child
342                     // is a text node.  Otherwise assume it is a geometry node.
343                     if(node.childNodes.length == 0 ||
344                        (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {
345                         if(this.extractAttributes) {
346                             name = "_attribute";
347                         }
348                     } else {
349                         name = "_geometry";
350                     }
351                 }
352                 if(name) {
353                     this.readers.feature[name].apply(this, [node, obj]);
354                 }
355             },
356             "_typeName": function(node, obj) {
357                 var container = {components: [], attributes: {}};
358                 this.readChildNodes(node, container);
359                 // look for common gml namespaced elements
360                 if(container.name) {
361                     container.attributes.name = container.name;
362                 }
363                 var feature = new OpenLayers.Feature.Vector(
364                     container.components[0], container.attributes
365                 );
366                 if (!this.singleFeatureType) {
367                     feature.type = node.nodeName.split(":").pop();
368                     feature.namespace = node.namespaceURI;
369                 }
370                 var fid = node.getAttribute("fid") ||
371                     this.getAttributeNS(node, this.namespaces["gml"], "id");
372                 if(fid) {
373                     feature.fid = fid;
374                 }
375                 if(this.internalProjection && this.externalProjection &&
376                    feature.geometry) {
377                     feature.geometry.transform(
378                         this.externalProjection, this.internalProjection
379                     );
380                 }
381                 if(container.bounds) {
382                     feature.geometry.bounds = container.bounds;
383                 }
384                 obj.features.push(feature);
385             },
386             "_geometry": function(node, obj) {
387                 this.readChildNodes(node, obj);
388             },
389             "_attribute": function(node, obj) {
390                 var local = node.localName || node.nodeName.split(":").pop();
391                 var value = this.getChildValue(node);
392                 obj.attributes[local] = value;
393             }
394         },
395         "wfs": {
396             "FeatureCollection": function(node, obj) {
397                 this.readChildNodes(node, obj);
398             }
399         }
400     },
401     
402     /**
403      * Method: write
404      *
405      * Parameters:
406      * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
407      *     An array of features or a single feature.
408      *
409      * Returns:
410      * {String} Given an array of features, a doc with a gml:featureMembers
411      *     element will be returned.  Given a single feature, a doc with a
412      *     gml:featureMember element will be returned.
413      */
414     write: function(features) {
415         var name;
416         if(features instanceof Array) {
417             name = "featureMembers";
418         } else {
419             name = "featureMember";
420         }
421         var root = this.writeNode("gml:" + name, features);
422         this.setAttributeNS(
423             root, this.namespaces["xsi"],
424             "xsi:schemaLocation", this.schemaLocation
425         );
426
427         return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
428     },
429     
430     /**
431      * Property: writers
432      * As a compliment to the readers property, this structure contains public
433      *     writing functions grouped by namespace alias and named like the
434      *     node names they produce.
435      */
436     writers: {
437         "gml": {
438             "featureMember": function(feature) {
439                 var node = this.createElementNSPlus("gml:featureMember");
440                 this.writeNode("feature:_typeName", feature, node);
441                 return node;
442             },
443             "MultiPoint": function(geometry) {
444                 var node = this.createElementNSPlus("gml:MultiPoint");
445                 for(var i=0; i<geometry.components.length; ++i) {
446                     this.writeNode("pointMember", geometry.components[i], node);
447                 }
448                 return node;
449             },
450             "pointMember": function(geometry) {
451                 var node = this.createElementNSPlus("gml:pointMember");
452                 this.writeNode("Point", geometry, node);
453                 return node;
454             },
455             "MultiLineString": function(geometry) {
456                 var node = this.createElementNSPlus("gml:MultiLineString");
457                 for(var i=0; i<geometry.components.length; ++i) {
458                     this.writeNode("lineStringMember", geometry.components[i], node);
459                 }
460                 return node;
461             },
462             "lineStringMember": function(geometry) {
463                 var node = this.createElementNSPlus("gml:lineStringMember");
464                 this.writeNode("LineString", geometry, node);
465                 return node;
466             },
467             "MultiPolygon": function(geometry) {
468                 var node = this.createElementNSPlus("gml:MultiPolygon");
469                 for(var i=0; i<geometry.components.length; ++i) {
470                     this.writeNode(
471                         "polygonMember", geometry.components[i], node
472                     );
473                 }
474                 return node;
475             },
476             "polygonMember": function(geometry) {
477                 var node = this.createElementNSPlus("gml:polygonMember");
478                 this.writeNode("Polygon", geometry, node);
479                 return node;
480             },
481             "GeometryCollection": function(geometry) {
482                 var node = this.createElementNSPlus("gml:GeometryCollection");
483                 for(var i=0, len=geometry.components.length; i<len; ++i) {
484                     this.writeNode("geometryMember", geometry.components[i], node);
485                 }
486                 return node;
487             },
488             "geometryMember": function(geometry) {
489                 var node = this.createElementNSPlus("gml:geometryMember");
490                 var child = this.writeNode("feature:_geometry", geometry);
491                 node.appendChild(child.firstChild);
492                 return node;
493             }
494         },
495         "feature": {
496             "_typeName": function(feature) {
497                 var node = this.createElementNSPlus("feature:" + this.featureType, {
498                     attributes: {fid: feature.fid}
499                 });
500                 if(feature.geometry) {
501                     this.writeNode("feature:_geometry", feature.geometry, node);
502                 }
503                 for(var name in feature.attributes) {
504                     var value = feature.attributes[name];
505                     if(value != null) {
506                         this.writeNode(
507                             "feature:_attribute",
508                             {name: name, value: value}, node
509                         );
510                     }
511                 }
512                 return node;
513             },
514             "_geometry": function(geometry) {
515                 if(this.externalProjection && this.internalProjection) {
516                     geometry = geometry.clone().transform(
517                         this.internalProjection, this.externalProjection
518                     );
519                 }    
520                 var node = this.createElementNSPlus(
521                     "feature:" + this.geometryName
522                 );
523                 var type = this.geometryTypes[geometry.CLASS_NAME];
524                 var child = this.writeNode("gml:" + type, geometry, node);
525                 if(this.srsName) {
526                     child.setAttribute("srsName", this.srsName);
527                 }
528                 return node;
529             },
530             "_attribute": function(obj) {
531                 return this.createElementNSPlus("feature:" + obj.name, {
532                     value: obj.value
533                 });
534             }
535         },
536         "wfs": {
537             "FeatureCollection": function(features) {
538                 /**
539                  * This is only here because GML2 only describes abstract
540                  * feature collections.  Typically, you would not be using
541                  * the GML format to write wfs elements.  This just provides
542                  * some way to write out lists of features.  GML3 defines the
543                  * featureMembers element, so that is used by default instead.
544                  */
545                 var node = this.createElementNSPlus("wfs:FeatureCollection");
546                 for(var i=0, len=features.length; i<len; ++i) {
547                     this.writeNode("gml:featureMember", features[i], node);
548                 }
549                 return node;
550             }
551         }
552     },
553     
554     /**
555      * Function: setGeometryTypes
556      * Sets the <geometryTypes> mapping.
557      */
558     setGeometryTypes: function() {
559         this.geometryTypes = {
560             "OpenLayers.Geometry.Point": "Point",
561             "OpenLayers.Geometry.MultiPoint": "MultiPoint",
562             "OpenLayers.Geometry.LineString": "LineString",
563             "OpenLayers.Geometry.MultiLineString": "MultiLineString",
564             "OpenLayers.Geometry.Polygon": "Polygon",
565             "OpenLayers.Geometry.MultiPolygon": "MultiPolygon",
566             "OpenLayers.Geometry.Collection": "GeometryCollection"
567         };
568     },
569
570     CLASS_NAME: "OpenLayers.Format.GML.Base" 
571
572 });