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. */
6 * @requires OpenLayers/Format/XML.js
7 * @requires OpenLayers/Format/GML.js
11 * Though required in the full build, if the GML format is excluded, we set
14 if(!OpenLayers.Format.GML) {
15 OpenLayers.Format.GML = {};
19 * Class: OpenLayers.Format.GML.Base
20 * Superclass for GML parsers.
23 * - <OpenLayers.Format.XML>
25 OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
28 * Property: namespaces
29 * {Object} Mapping of namespace aliases to namespace URIs.
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
39 * Property: defaultPrefix
44 * Property: schemaLocation
45 * {String} Schema location for a particular minor version.
50 * APIProperty: featureType
51 * {Array(String) or String} The local (without prefix) feature typeName(s).
56 * APIProperty: featureNS
57 * {String} The feature namespace. Must be set in the options at
63 * APIProperty: geometry
64 * {String} Name of geometry element. Defaults to "geometry".
66 geometryName: "geometry",
69 * APIProperty: extractAttributes
70 * {Boolean} Extract attributes from GML. Default is true.
72 extractAttributes: true,
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.
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.
91 * Property: geometryTypes
92 * {Object} Maps OpenLayers geometry class names to GML element names.
93 * Use <setGeometryTypes> before accessing this property.
98 * Property: singleFeatureType
99 * {Boolean} True if there is only 1 featureType, and not an array
102 singleFeatureType: null,
106 * Compiled regular expressions for manipulating strings.
109 trimSpace: (/^\s*|\s*$/g),
110 removeSpace: (/\s*/g),
112 trimComma: (/\s*,\s*/g)
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
122 * options - {Object} An optional object whose properties will be set on
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.
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);
137 this.singleFeatureType = !options || (typeof options.featureType === "string");
144 * data - {DOMElement} A gml:featureMember element, a gml:featureMembers
145 * element, or an element containing either of the above at any level.
148 * {Array(<OpenLayers.Feature.Vector>)} An array of features.
150 read: function(data) {
151 if(typeof data == "string") {
152 data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
154 if(data && data.nodeType == 9) {
155 data = data.documentElement;
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"
164 if(elements.length) {
165 for(var i=0, len=elements.length; i<len; ++i) {
166 this.readNode(elements[i], {features: features});
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"
173 if(elements.length) {
174 // there can be only one
175 this.readNode(elements[0], {features: features});
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
192 "featureMember": function(node, obj) {
193 this.readChildNodes(node, obj);
195 "featureMembers": function(node, obj) {
196 this.readChildNodes(node, obj);
198 "name": function(node, obj) {
199 obj.name = this.getChildValue(node);
201 "boundedBy": function(node, obj) {
203 this.readChildNodes(node, container);
204 if(container.components && container.components.length > 0) {
205 obj.bounds = container.components[0];
208 "Point": function(node, container) {
209 var obj = {points: []};
210 this.readChildNodes(node, obj);
211 if(!container.components) {
212 container.components = [];
214 container.components.push(obj.points[0]);
216 "coordinates": function(node, obj) {
217 var str = this.getChildValue(node).replace(
218 this.regExes.trimSpace, ""
220 str = str.replace(this.regExes.trimComma, ",");
221 var pointList = str.split(this.regExes.splitSpace);
223 var numPoints = pointList.length;
224 var points = new Array(numPoints);
225 for(var i=0; i<numPoints; ++i) {
226 coords = pointList[i].split(",");
228 points[i] = new OpenLayers.Geometry.Point(
229 coords[0], coords[1], coords[2]
232 points[i] = new OpenLayers.Geometry.Point(
233 coords[1], coords[0], coords[2]
239 "coord": function(node, obj) {
241 this.readChildNodes(node, coord);
245 obj.points.push(new OpenLayers.Geometry.Point(
246 coord.x, coord.y, coord.z
249 "X": function(node, coord) {
250 coord.x = this.getChildValue(node);
252 "Y": function(node, coord) {
253 coord.y = this.getChildValue(node);
255 "Z": function(node, coord) {
256 coord.z = this.getChildValue(node);
258 "MultiPoint": function(node, container) {
259 var obj = {components: []};
260 this.readChildNodes(node, obj);
261 container.components = [
262 new OpenLayers.Geometry.MultiPoint(obj.components)
265 "pointMember": function(node, obj) {
266 this.readChildNodes(node, obj);
268 "LineString": function(node, container) {
270 this.readChildNodes(node, obj);
271 if(!container.components) {
272 container.components = [];
274 container.components.push(
275 new OpenLayers.Geometry.LineString(obj.points)
278 "MultiLineString": function(node, container) {
279 var obj = {components: []};
280 this.readChildNodes(node, obj);
281 container.components = [
282 new OpenLayers.Geometry.MultiLineString(obj.components)
285 "lineStringMember": function(node, obj) {
286 this.readChildNodes(node, obj);
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 = [];
295 container.components.push(
296 new OpenLayers.Geometry.Polygon(obj.inner)
299 "LinearRing": function(node, obj) {
301 this.readChildNodes(node, container);
302 obj.components = [new OpenLayers.Geometry.LinearRing(
306 "MultiPolygon": function(node, container) {
307 var obj = {components: []};
308 this.readChildNodes(node, obj);
309 container.components = [
310 new OpenLayers.Geometry.MultiPolygon(obj.components)
313 "polygonMember": function(node, obj) {
314 this.readChildNodes(node, obj);
316 "GeometryCollection": function(node, container) {
317 var obj = {components: []};
318 this.readChildNodes(node, obj);
319 container.components = [
320 new OpenLayers.Geometry.Collection(obj.components)
323 "geometryMember": function(node, obj) {
324 this.readChildNodes(node, obj);
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.
333 var local = node.localName || node.nodeName.split(":").pop();
334 if (!this.singleFeatureType &&
335 (OpenLayers.Util.indexOf(this.featureType, local) != -1)) {
338 else if(local == this.featureType) {
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) {
353 this.readers.feature[name].apply(this, [node, obj]);
356 "_typeName": function(node, obj) {
357 var container = {components: [], attributes: {}};
358 this.readChildNodes(node, container);
359 // look for common gml namespaced elements
361 container.attributes.name = container.name;
363 var feature = new OpenLayers.Feature.Vector(
364 container.components[0], container.attributes
366 if (!this.singleFeatureType) {
367 feature.type = node.nodeName.split(":").pop();
368 feature.namespace = node.namespaceURI;
370 var fid = node.getAttribute("fid") ||
371 this.getAttributeNS(node, this.namespaces["gml"], "id");
375 if(this.internalProjection && this.externalProjection &&
377 feature.geometry.transform(
378 this.externalProjection, this.internalProjection
381 if(container.bounds) {
382 feature.geometry.bounds = container.bounds;
384 obj.features.push(feature);
386 "_geometry": function(node, obj) {
387 this.readChildNodes(node, obj);
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;
396 "FeatureCollection": function(node, obj) {
397 this.readChildNodes(node, obj);
406 * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
407 * An array of features or a single feature.
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.
414 write: function(features) {
416 if(features instanceof Array) {
417 name = "featureMembers";
419 name = "featureMember";
421 var root = this.writeNode("gml:" + name, features);
423 root, this.namespaces["xsi"],
424 "xsi:schemaLocation", this.schemaLocation
427 return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
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.
438 "featureMember": function(feature) {
439 var node = this.createElementNSPlus("gml:featureMember");
440 this.writeNode("feature:_typeName", feature, node);
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);
450 "pointMember": function(geometry) {
451 var node = this.createElementNSPlus("gml:pointMember");
452 this.writeNode("Point", geometry, node);
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);
462 "lineStringMember": function(geometry) {
463 var node = this.createElementNSPlus("gml:lineStringMember");
464 this.writeNode("LineString", geometry, node);
467 "MultiPolygon": function(geometry) {
468 var node = this.createElementNSPlus("gml:MultiPolygon");
469 for(var i=0; i<geometry.components.length; ++i) {
471 "polygonMember", geometry.components[i], node
476 "polygonMember": function(geometry) {
477 var node = this.createElementNSPlus("gml:polygonMember");
478 this.writeNode("Polygon", geometry, node);
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);
488 "geometryMember": function(geometry) {
489 var node = this.createElementNSPlus("gml:geometryMember");
490 var child = this.writeNode("feature:_geometry", geometry);
491 node.appendChild(child.firstChild);
496 "_typeName": function(feature) {
497 var node = this.createElementNSPlus("feature:" + this.featureType, {
498 attributes: {fid: feature.fid}
500 if(feature.geometry) {
501 this.writeNode("feature:_geometry", feature.geometry, node);
503 for(var name in feature.attributes) {
504 var value = feature.attributes[name];
507 "feature:_attribute",
508 {name: name, value: value}, node
514 "_geometry": function(geometry) {
515 if(this.externalProjection && this.internalProjection) {
516 geometry = geometry.clone().transform(
517 this.internalProjection, this.externalProjection
520 var node = this.createElementNSPlus(
521 "feature:" + this.geometryName
523 var type = this.geometryTypes[geometry.CLASS_NAME];
524 var child = this.writeNode("gml:" + type, geometry, node);
526 child.setAttribute("srsName", this.srsName);
530 "_attribute": function(obj) {
531 return this.createElementNSPlus("feature:" + obj.name, {
537 "FeatureCollection": function(features) {
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.
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);
555 * Function: setGeometryTypes
556 * Sets the <geometryTypes> mapping.
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"
570 CLASS_NAME: "OpenLayers.Format.GML.Base"