Project

General

Profile

Download (24.8 KB) Statistics
| Branch: | Tag: | Revision:
1
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2
 * full list of contributors). Published under the 2-clause BSD license.
3
 * See license.txt in the OpenLayers distribution or repository for the
4
 * full text of the license. */
5

    
6
/**
7
 * @requires OpenLayers/Format/XML.js
8
 * @requires OpenLayers/Format/GML.js
9
 */
10

    
11
/**
12
 * Though required in the full build, if the GML format is excluded, we set
13
 * the namespace here.
14
 */
15
if(!OpenLayers.Format.GML) {
16
    OpenLayers.Format.GML = {};
17
}
18

    
19
/**
20
 * Class: OpenLayers.Format.GML.Base
21
 * Superclass for GML parsers.
22
 *
23
 * Inherits from:
24
 *  - <OpenLayers.Format.XML>
25
 */
26
OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
27
    
28
    /**
29
     * Property: namespaces
30
     * {Object} Mapping of namespace aliases to namespace URIs.
31
     */
32
    namespaces: {
33
        gml: "http://www.opengis.net/gml",
34
        xlink: "http://www.w3.org/1999/xlink",
35
        xsi: "http://www.w3.org/2001/XMLSchema-instance",
36
        wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
37
    },
38
    
39
    /**
40
     * Property: defaultPrefix
41
     */
42
    defaultPrefix: "gml",
43

    
44
    /**
45
     * Property: schemaLocation
46
     * {String} Schema location for a particular minor version.
47
     */
48
    schemaLocation: null,
49
    
50
    /**
51
     * APIProperty: featureType
52
     * {Array(String) or String} The local (without prefix) feature typeName(s).
53
     */
54
    featureType: null,
55
    
56
    /**
57
     * APIProperty: featureNS
58
     * {String} The feature namespace.  Must be set in the options at
59
     *     construction.
60
     */
61
    featureNS: null,
62

    
63
    /**
64
     * APIProperty: geometry
65
     * {String} Name of geometry element.  Defaults to "geometry". If null, it
66
     * will be set on <read> when the first geometry is parsed.
67
     */
68
    geometryName: "geometry",
69

    
70
    /**
71
     * APIProperty: extractAttributes
72
     * {Boolean} Extract attributes from GML.  Default is true.
73
     */
74
    extractAttributes: true,
75
    
76
    /**
77
     * APIProperty: srsName
78
     * {String} URI for spatial reference system.  This is optional for
79
     *     single part geometries and mandatory for collections and multis.
80
     *     If set, the srsName attribute will be written for all geometries.
81
     *     Default is null.
82
     */
83
    srsName: null,
84

    
85
    /**
86
     * APIProperty: xy
87
     * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
88
     * Changing is not recommended, a new Format should be instantiated.
89
     */ 
90
    xy: true,
91

    
92
    /**
93
     * Property: geometryTypes
94
     * {Object} Maps OpenLayers geometry class names to GML element names.
95
     *     Use <setGeometryTypes> before accessing this property.
96
     */
97
    geometryTypes: null,
98

    
99
    /**
100
     * Property: singleFeatureType
101
     * {Boolean} True if there is only 1 featureType, and not an array
102
     *     of featuretypes.
103
     */
104
    singleFeatureType: null,
105
    
106
    /**
107
     * Property: autoConfig
108
     * {Boolean} Indicates if the format was configured without a <featureNS>,
109
     * but auto-configured <featureNS> and <featureType> during read.
110
     * Subclasses making use of <featureType> auto-configuration should make
111
     * the first call to the <readNode> method (usually in the read method)
112
     * with true as 3rd argument, so the auto-configured featureType can be
113
     * reset and the format can be reused for subsequent reads with data from
114
     * different featureTypes. Set to false after read if you want to keep the
115
     * auto-configured values.
116
     */
117

    
118
    /**
119
     * Property: regExes
120
     * Compiled regular expressions for manipulating strings.
121
     */
122
    regExes: {
123
        trimSpace: (/^\s*|\s*$/g),
124
        removeSpace: (/\s*/g),
125
        splitSpace: (/\s+/),
126
        trimComma: (/\s*,\s*/g),
127
        featureMember: (/^(.*:)?featureMembers?$/)
128
    },
129

    
130
    /**
131
     * Constructor: OpenLayers.Format.GML.Base
132
     * Instances of this class are not created directly.  Use the
133
     *     <OpenLayers.Format.GML.v2> or <OpenLayers.Format.GML.v3> constructor
134
     *     instead.
135
     *
136
     * Parameters:
137
     * options - {Object} An optional object whose properties will be set on
138
     *     this instance.
139
     *
140
     * Valid options properties:
141
     * featureType - {Array(String) or String} Local (without prefix) feature 
142
     *     typeName(s) (required for write).
143
     * featureNS - {String} Feature namespace (required for write).
144
     * geometryName - {String} Geometry element name (required for write).
145
     */
146
    initialize: function(options) {
147
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
148
        this.setGeometryTypes();
149
        if(options && options.featureNS) {
150
            this.setNamespace("feature", options.featureNS);
151
        }
152
        this.singleFeatureType = !options || (typeof options.featureType === "string");
153
    },
154
    
155
    /**
156
     * Method: read
157
     *
158
     * Parameters:
159
     * data - {DOMElement} A gml:featureMember element, a gml:featureMembers
160
     *     element, or an element containing either of the above at any level.
161
     *
162
     * Returns:
163
     * {Array(<OpenLayers.Feature.Vector>)} An array of features.
164
     */
165
    read: function(data) {
166
        if(typeof data == "string") { 
167
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
168
        }
169
        if(data && data.nodeType == 9) {
170
            data = data.documentElement;
171
        }
172
        var features = [];
173
        this.readNode(data, {features: features}, true);
174
        if(features.length == 0) {
175
            // look for gml:featureMember elements
176
            var elements = this.getElementsByTagNameNS(
177
                data, this.namespaces.gml, "featureMember"
178
            );
179
            if(elements.length) {
180
                for(var i=0, len=elements.length; i<len; ++i) {
181
                    this.readNode(elements[i], {features: features}, true);
182
                }
183
            } else {
184
                // look for gml:featureMembers elements (this is v3, but does no harm here)
185
                var elements = this.getElementsByTagNameNS(
186
                    data, this.namespaces.gml, "featureMembers"
187
                );
188
                if(elements.length) {
189
                    // there can be only one
190
                    this.readNode(elements[0], {features: features}, true);
191
                }
192
            }
193
        }
194
        return features;
195
    },
196
    
197
    /**
198
     * Method: readNode
199
     * Shorthand for applying one of the named readers given the node
200
     *     namespace and local name.  Readers take two args (node, obj) and
201
     *     generally extend or modify the second.
202
     *
203
     * Parameters:
204
     * node - {DOMElement} The node to be read (required).
205
     * obj - {Object} The object to be modified (optional).
206
     * first - {Boolean} Should be set to true for the first node read. This
207
     *     is usually the readNode call in the read method. Without this being
208
     *     set, auto-configured properties will stick on subsequent reads.
209
     *
210
     * Returns:
211
     * {Object} The input object, modified (or a new one if none was provided).
212
     */
213
    readNode: function(node, obj, first) {
214
        // on subsequent calls of format.read(), we want to reset auto-
215
        // configured properties and auto-configure again.
216
        if (first === true && this.autoConfig === true) {
217
            this.featureType = null;
218
            delete this.namespaceAlias[this.featureNS];
219
            delete this.namespaces["feature"];
220
            this.featureNS = null;
221
        }
222
        // featureType auto-configuration
223
        if (!this.featureNS && (!(node.prefix in this.namespaces) &&
224
                node.parentNode.namespaceURI == this.namespaces["gml"] &&
225
                this.regExes.featureMember.test(node.parentNode.nodeName))) {
226
            this.featureType = node.nodeName.split(":").pop();
227
            this.setNamespace("feature", node.namespaceURI);
228
            this.featureNS = node.namespaceURI;
229
            this.autoConfig = true;
230
        }
231
        return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]);
232
    },
233
    
234
    /**
235
     * Property: readers
236
     * Contains public functions, grouped by namespace prefix, that will
237
     *     be applied when a namespaced node is found matching the function
238
     *     name.  The function will be applied in the scope of this parser
239
     *     with two arguments: the node being read and a context object passed
240
     *     from the parent.
241
     */
242
    readers: {
243
        "gml": {
244
            "_inherit": function(node, obj, container) {
245
                // To be implemented by version specific parsers
246
            },
247
            "featureMember": function(node, obj) {
248
                this.readChildNodes(node, obj);
249
            },
250
            "featureMembers": function(node, obj) {
251
                this.readChildNodes(node, obj);                
252
            },
253
            "name": function(node, obj) {
254
                obj.name = this.getChildValue(node);
255
            },
256
            "boundedBy": function(node, obj) {
257
                var container = {};
258
                this.readChildNodes(node, container);
259
                if(container.components && container.components.length > 0) {
260
                    obj.bounds = container.components[0];
261
                }
262
            },
263
            "Point": function(node, container) {
264
                var obj = {points: []};
265
                this.readChildNodes(node, obj);
266
                if(!container.components) {
267
                    container.components = [];
268
                }
269
                container.components.push(obj.points[0]);
270
            },
271
            "coordinates": function(node, obj) {
272
                var str = this.getChildValue(node).replace(
273
                    this.regExes.trimSpace, ""
274
                );
275
                str = str.replace(this.regExes.trimComma, ",");
276
                var pointList = str.split(this.regExes.splitSpace);
277
                var coords;
278
                var numPoints = pointList.length;
279
                var points = new Array(numPoints);
280
                for(var i=0; i<numPoints; ++i) {
281
                    coords = pointList[i].split(",");
282
                    if (this.xy) {
283
                        points[i] = new OpenLayers.Geometry.Point(
284
                            coords[0], coords[1], coords[2]
285
                        );
286
                    } else {
287
                        points[i] = new OpenLayers.Geometry.Point(
288
                            coords[1], coords[0], coords[2]
289
                        );
290
                    }
291
                }
292
                obj.points = points;
293
            },
294
            "coord": function(node, obj) {
295
                var coord = {};
296
                this.readChildNodes(node, coord);
297
                if(!obj.points) {
298
                    obj.points = [];
299
                }
300
                obj.points.push(new OpenLayers.Geometry.Point(
301
                    coord.x, coord.y, coord.z
302
                ));
303
            },
304
            "X": function(node, coord) {
305
                coord.x = this.getChildValue(node);
306
            },
307
            "Y": function(node, coord) {
308
                coord.y = this.getChildValue(node);
309
            },
310
            "Z": function(node, coord) {
311
                coord.z = this.getChildValue(node);
312
            },
313
            "MultiPoint": function(node, container) {
314
                var obj = {components: []};
315
                this.readers.gml._inherit.apply(this, [node, obj, container]);
316
                this.readChildNodes(node, obj);
317
                container.components = [
318
                    new OpenLayers.Geometry.MultiPoint(obj.components)
319
                ];
320
            },
321
            "pointMember": function(node, obj) {
322
                this.readChildNodes(node, obj);
323
            },
324
            "LineString": function(node, container) {
325
                var obj = {};
326
                this.readers.gml._inherit.apply(this, [node, obj, container]);
327
                this.readChildNodes(node, obj);
328
                if(!container.components) {
329
                    container.components = [];
330
                }
331
                container.components.push(
332
                    new OpenLayers.Geometry.LineString(obj.points)
333
                );
334
            },
335
            "MultiLineString": function(node, container) {
336
                var obj = {components: []};
337
                this.readers.gml._inherit.apply(this, [node, obj, container]);
338
                this.readChildNodes(node, obj);
339
                container.components = [
340
                    new OpenLayers.Geometry.MultiLineString(obj.components)
341
                ];
342
            },
343
            "lineStringMember": function(node, obj) {
344
                this.readChildNodes(node, obj);
345
            },
346
            "Polygon": function(node, container) {
347
                var obj = {outer: null, inner: []};
348
                this.readers.gml._inherit.apply(this, [node, obj, container]);
349
                this.readChildNodes(node, obj);
350
                obj.inner.unshift(obj.outer);
351
                if(!container.components) {
352
                    container.components = [];
353
                }
354
                container.components.push(
355
                    new OpenLayers.Geometry.Polygon(obj.inner)
356
                );
357
            },
358
            "LinearRing": function(node, obj) {
359
                var container = {};
360
                this.readers.gml._inherit.apply(this, [node, container]);
361
                this.readChildNodes(node, container);
362
                obj.components = [new OpenLayers.Geometry.LinearRing(
363
                    container.points
364
                )];
365
            },
366
            "MultiPolygon": function(node, container) {
367
                var obj = {components: []};
368
                this.readers.gml._inherit.apply(this, [node, obj, container]);
369
                this.readChildNodes(node, obj);
370
                container.components = [
371
                    new OpenLayers.Geometry.MultiPolygon(obj.components)
372
                ];
373
            },
374
            "polygonMember": function(node, obj) {
375
                this.readChildNodes(node, obj);
376
            },
377
            "GeometryCollection": function(node, container) {
378
                var obj = {components: []};
379
                this.readers.gml._inherit.apply(this, [node, obj, container]);
380
                this.readChildNodes(node, obj);
381
                container.components = [
382
                    new OpenLayers.Geometry.Collection(obj.components)
383
                ];
384
            },
385
            "geometryMember": function(node, obj) {
386
                this.readChildNodes(node, obj);
387
            }
388
        },
389
        "feature": {
390
            "*": function(node, obj) {
391
                // The node can either be named like the featureType, or it
392
                // can be a child of the feature:featureType.  Children can be
393
                // geometry or attributes.
394
                var name;
395
                var local = node.localName || node.nodeName.split(":").pop();
396
                // Since an attribute can have the same name as the feature type
397
                // we only want to read the node as a feature if the parent
398
                // node can have feature nodes as children.  In this case, the
399
                // obj.features property is set.
400
                if (obj.features) {
401
                    if (!this.singleFeatureType &&
402
                        (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) {
403
                        name = "_typeName";
404
                    } else if(local === this.featureType) {
405
                        name = "_typeName";
406
                    }
407
                } else {
408
                    // Assume attribute elements have one child node and that the child
409
                    // is a text node.  Otherwise assume it is a geometry node.
410
                    if(node.childNodes.length == 0 ||
411
                       (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {
412
                        if(this.extractAttributes) {
413
                            name = "_attribute";
414
                        }
415
                    } else {
416
                        name = "_geometry";
417
                    }
418
                }
419
                if(name) {
420
                    this.readers.feature[name].apply(this, [node, obj]);
421
                }
422
            },
423
            "_typeName": function(node, obj) {
424
                var container = {components: [], attributes: {}};
425
                this.readChildNodes(node, container);
426
                // look for common gml namespaced elements
427
                if(container.name) {
428
                    container.attributes.name = container.name;
429
                }
430
                var feature = new OpenLayers.Feature.Vector(
431
                    container.components[0], container.attributes
432
                );
433
                if (!this.singleFeatureType) {
434
                    feature.type = node.nodeName.split(":").pop();
435
                    feature.namespace = node.namespaceURI;
436
                }
437
                var fid = node.getAttribute("fid") ||
438
                    this.getAttributeNS(node, this.namespaces["gml"], "id");
439
                if(fid) {
440
                    feature.fid = fid;
441
                }
442
                if(this.internalProjection && this.externalProjection &&
443
                   feature.geometry) {
444
                    feature.geometry.transform(
445
                        this.externalProjection, this.internalProjection
446
                    );
447
                }
448
                if(container.bounds) {
449
                    feature.bounds = container.bounds;
450
                }
451
                obj.features.push(feature);
452
            },
453
            "_geometry": function(node, obj) {
454
                if (!this.geometryName) {
455
                    this.geometryName = node.nodeName.split(":").pop();
456
                }
457
                this.readChildNodes(node, obj);
458
            },
459
            "_attribute": function(node, obj) {
460
                var local = node.localName || node.nodeName.split(":").pop();
461
                var value = this.getChildValue(node);
462
                obj.attributes[local] = value;
463
            }
464
        },
465
        "wfs": {
466
            "FeatureCollection": function(node, obj) {
467
                this.readChildNodes(node, obj);
468
            }
469
        }
470
    },
471
    
472
    /**
473
     * Method: write
474
     *
475
     * Parameters:
476
     * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
477
     *     An array of features or a single feature.
478
     *
479
     * Returns:
480
     * {String} Given an array of features, a doc with a gml:featureMembers
481
     *     element will be returned.  Given a single feature, a doc with a
482
     *     gml:featureMember element will be returned.
483
     */
484
    write: function(features) {
485
        var name;
486
        if(OpenLayers.Util.isArray(features)) {
487
            name = "featureMembers";
488
        } else {
489
            name = "featureMember";
490
        }
491
        var root = this.writeNode("gml:" + name, features);
492
        this.setAttributeNS(
493
            root, this.namespaces["xsi"],
494
            "xsi:schemaLocation", this.schemaLocation
495
        );
496

    
497
        return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
498
    },
499
    
500
    /**
501
     * Property: writers
502
     * As a compliment to the readers property, this structure contains public
503
     *     writing functions grouped by namespace alias and named like the
504
     *     node names they produce.
505
     */
506
    writers: {
507
        "gml": {
508
            "featureMember": function(feature) {
509
                var node = this.createElementNSPlus("gml:featureMember");
510
                this.writeNode("feature:_typeName", feature, node);
511
                return node;
512
            },
513
            "MultiPoint": function(geometry) {
514
                var node = this.createElementNSPlus("gml:MultiPoint");
515
                var components = geometry.components || [geometry];
516
                for(var i=0, ii=components.length; i<ii; ++i) {
517
                    this.writeNode("pointMember", components[i], node);
518
                }
519
                return node;
520
            },
521
            "pointMember": function(geometry) {
522
                var node = this.createElementNSPlus("gml:pointMember");
523
                this.writeNode("Point", geometry, node);
524
                return node;
525
            },
526
            "MultiLineString": function(geometry) {
527
                var node = this.createElementNSPlus("gml:MultiLineString");
528
                var components = geometry.components || [geometry];
529
                for(var i=0, ii=components.length; i<ii; ++i) {
530
                    this.writeNode("lineStringMember", components[i], node);
531
                }
532
                return node;
533
            },
534
            "lineStringMember": function(geometry) {
535
                var node = this.createElementNSPlus("gml:lineStringMember");
536
                this.writeNode("LineString", geometry, node);
537
                return node;
538
            },
539
            "MultiPolygon": function(geometry) {
540
                var node = this.createElementNSPlus("gml:MultiPolygon");
541
                var components = geometry.components || [geometry];
542
                for(var i=0, ii=components.length; i<ii; ++i) {
543
                    this.writeNode(
544
                        "polygonMember", components[i], node
545
                    );
546
                }
547
                return node;
548
            },
549
            "polygonMember": function(geometry) {
550
                var node = this.createElementNSPlus("gml:polygonMember");
551
                this.writeNode("Polygon", geometry, node);
552
                return node;
553
            },
554
            "GeometryCollection": function(geometry) {
555
                var node = this.createElementNSPlus("gml:GeometryCollection");
556
                for(var i=0, len=geometry.components.length; i<len; ++i) {
557
                    this.writeNode("geometryMember", geometry.components[i], node);
558
                }
559
                return node;
560
            },
561
            "geometryMember": function(geometry) {
562
                var node = this.createElementNSPlus("gml:geometryMember");
563
                var child = this.writeNode("feature:_geometry", geometry);
564
                node.appendChild(child.firstChild);
565
                return node;
566
            }
567
        },
568
        "feature": {
569
            "_typeName": function(feature) {
570
                var node = this.createElementNSPlus("feature:" + this.featureType, {
571
                    attributes: {fid: feature.fid}
572
                });
573
                if(feature.geometry) {
574
                    this.writeNode("feature:_geometry", feature.geometry, node);
575
                }
576
                for(var name in feature.attributes) {
577
                    var value = feature.attributes[name];
578
                    if(value != null) {
579
                        this.writeNode(
580
                            "feature:_attribute",
581
                            {name: name, value: value}, node
582
                        );
583
                    }
584
                }
585
                return node;
586
            },
587
            "_geometry": function(geometry) {
588
                if(this.externalProjection && this.internalProjection) {
589
                    geometry = geometry.clone().transform(
590
                        this.internalProjection, this.externalProjection
591
                    );
592
                }    
593
                var node = this.createElementNSPlus(
594
                    "feature:" + this.geometryName
595
                );
596
                var type = this.geometryTypes[geometry.CLASS_NAME];
597
                var child = this.writeNode("gml:" + type, geometry, node);
598
                if(this.srsName) {
599
                    child.setAttribute("srsName", this.srsName);
600
                }
601
                return node;
602
            },
603
            "_attribute": function(obj) {
604
                return this.createElementNSPlus("feature:" + obj.name, {
605
                    value: obj.value
606
                });
607
            }
608
        },
609
        "wfs": {
610
            "FeatureCollection": function(features) {
611
                /**
612
                 * This is only here because GML2 only describes abstract
613
                 * feature collections.  Typically, you would not be using
614
                 * the GML format to write wfs elements.  This just provides
615
                 * some way to write out lists of features.  GML3 defines the
616
                 * featureMembers element, so that is used by default instead.
617
                 */
618
                var node = this.createElementNSPlus("wfs:FeatureCollection");
619
                for(var i=0, len=features.length; i<len; ++i) {
620
                    this.writeNode("gml:featureMember", features[i], node);
621
                }
622
                return node;
623
            }
624
        }
625
    },
626
    
627
    /**
628
     * Method: setGeometryTypes
629
     * Sets the <geometryTypes> mapping.
630
     */
631
    setGeometryTypes: function() {
632
        this.geometryTypes = {
633
            "OpenLayers.Geometry.Point": "Point",
634
            "OpenLayers.Geometry.MultiPoint": "MultiPoint",
635
            "OpenLayers.Geometry.LineString": "LineString",
636
            "OpenLayers.Geometry.MultiLineString": "MultiLineString",
637
            "OpenLayers.Geometry.Polygon": "Polygon",
638
            "OpenLayers.Geometry.MultiPolygon": "MultiPolygon",
639
            "OpenLayers.Geometry.Collection": "GeometryCollection"
640
        };
641
    },
642

    
643
    CLASS_NAME: "OpenLayers.Format.GML.Base" 
644

    
645
});
(1-1/3)