Project

General

Profile

Download (12.7 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/Feature/Vector.js
9
 * @requires OpenLayers/Geometry/Point.js
10
 * @requires OpenLayers/Geometry/LineString.js
11
 * @requires OpenLayers/Projection.js
12
 */
13

    
14
/**
15
 * Class: OpenLayers.Format.GPX
16
 * Read/write GPX parser. Create a new instance with the 
17
 *     <OpenLayers.Format.GPX> constructor.
18
 *
19
 * Inherits from:
20
 *  - <OpenLayers.Format.XML>
21
 */
22
OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {
23
    
24

    
25
    /** 
26
     * APIProperty: defaultDesc
27
     * {String} Default description for the waypoints/tracks in the case
28
     *     where the feature has no "description" attribute.
29
     *     Default is "No description available".
30
     */
31
    defaultDesc: "No description available",
32

    
33
   /**
34
    * APIProperty: extractWaypoints
35
    * {Boolean} Extract waypoints from GPX. (default: true)
36
    */
37
    extractWaypoints: true,
38
    
39
   /**
40
    * APIProperty: extractTracks
41
    * {Boolean} Extract tracks from GPX. (default: true)
42
    */
43
    extractTracks: true,
44
    
45
   /**
46
    * APIProperty: extractRoutes
47
    * {Boolean} Extract routes from GPX. (default: true)
48
    */
49
    extractRoutes: true,
50
    
51
    /**
52
     * APIProperty: extractAttributes
53
     * {Boolean} Extract feature attributes from GPX. (default: true)
54
     *     NOTE: Attributes as part of extensions to the GPX standard may not
55
     *     be extracted.
56
     */
57
    extractAttributes: true,
58

    
59
    /**
60
     * Property: namespaces
61
     * {Object} Mapping of namespace aliases to namespace URIs.
62
     */
63
    namespaces: {
64
        gpx: "http://www.topografix.com/GPX/1/1",
65
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
66
    },
67

    
68
    /**
69
     * Property: schemaLocation
70
     * {String} Schema location. Defaults to
71
     *  "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
72
     */
73
    schemaLocation: "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd",
74

    
75
    /**
76
     * APIProperty: creator
77
     * {String} The creator attribute to be added to the written GPX files.
78
     * Defaults to "OpenLayers"
79
     */
80
    creator: "OpenLayers",
81
    
82
    /**
83
     * Constructor: OpenLayers.Format.GPX
84
     * Create a new parser for GPX.
85
     *
86
     * Parameters:
87
     * options - {Object} An optional object whose properties will be set on
88
     *     this instance.
89
     */
90
    initialize: function(options) {
91
        // GPX coordinates are always in longlat WGS84
92
        this.externalProjection = new OpenLayers.Projection("EPSG:4326");
93

    
94
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
95
    },
96
    
97
    /**
98
     * APIMethod: read
99
     * Return a list of features from a GPX doc
100
     *
101
     * Parameters:
102
     * doc - {Element} 
103
     *
104
     * Returns:
105
     * Array({<OpenLayers.Feature.Vector>})
106
     */
107
    read: function(doc) {
108
        if (typeof doc == "string") { 
109
            doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
110
        }
111
        var features = [];
112
        
113
        if(this.extractTracks) {
114
            var tracks = doc.getElementsByTagName("trk");
115
            for (var i=0, len=tracks.length; i<len; i++) {
116
                // Attributes are only in trk nodes, not trkseg nodes
117
                var attrs = {};
118
                if(this.extractAttributes) {
119
                    attrs = this.parseAttributes(tracks[i]);
120
                }
121
                
122
                var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, "trkseg");
123
                for (var j = 0, seglen = segs.length; j < seglen; j++) {
124
                    // We don't yet support extraction of trkpt attributes
125
                    // All trksegs of a trk get that trk's attributes
126
                    var track = this.extractSegment(segs[j], "trkpt");
127
                    features.push(new OpenLayers.Feature.Vector(track, attrs));
128
                }
129
            }
130
        }
131
        
132
        if(this.extractRoutes) {
133
            var routes = doc.getElementsByTagName("rte");
134
            for (var k=0, klen=routes.length; k<klen; k++) {
135
                var attrs = {};
136
                if(this.extractAttributes) {
137
                    attrs = this.parseAttributes(routes[k]);
138
                }
139
                var route = this.extractSegment(routes[k], "rtept");
140
                features.push(new OpenLayers.Feature.Vector(route, attrs));
141
            }
142
        }
143
        
144
        if(this.extractWaypoints) {
145
            var waypoints = doc.getElementsByTagName("wpt");
146
            for (var l = 0, len = waypoints.length; l < len; l++) {
147
                var attrs = {};
148
                if(this.extractAttributes) {
149
                    attrs = this.parseAttributes(waypoints[l]);
150
                }
151
                var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute("lon"), waypoints[l].getAttribute("lat"));
152
                features.push(new OpenLayers.Feature.Vector(wpt, attrs));
153
            }
154
        }
155
        
156
        if (this.internalProjection && this.externalProjection) {
157
            for (var g = 0, featLength = features.length; g < featLength; g++) {
158
                features[g].geometry.transform(this.externalProjection,
159
                                    this.internalProjection);
160
            }
161
        }
162
        
163
        return features;
164
    },
165
    
166
   /**
167
    * Method: extractSegment
168
    *
169
    * Parameters:
170
    * segment - {DOMElement} a trkseg or rte node to parse
171
    * segmentType - {String} nodeName of waypoints that form the line
172
    *
173
    * Returns:
174
    * {<OpenLayers.Geometry.LineString>} A linestring geometry
175
    */
176
    extractSegment: function(segment, segmentType) {
177
        var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);
178
        var point_features = [];
179
        for (var i = 0, len = points.length; i < len; i++) {
180
            point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute("lon"), points[i].getAttribute("lat")));
181
        }
182
        return new OpenLayers.Geometry.LineString(point_features);
183
    },
184
    
185
    /**
186
     * Method: parseAttributes
187
     *
188
     * Parameters:
189
     * node - {<DOMElement>}
190
     *
191
     * Returns:
192
     * {Object} An attributes object.
193
     */
194
    parseAttributes: function(node) {
195
        // node is either a wpt, trk or rte
196
        // attributes are children of the form <attr>value</attr>
197
        var attributes = {};
198
        var attrNode = node.firstChild, value, name;
199
        while(attrNode) {
200
            if(attrNode.nodeType == 1 && attrNode.firstChild) {
201
                value = attrNode.firstChild;
202
                if(value.nodeType == 3 || value.nodeType == 4) {
203
                    name = (attrNode.prefix) ?
204
                        attrNode.nodeName.split(":")[1] :
205
                        attrNode.nodeName;
206
                    if(name != "trkseg" && name != "rtept") {
207
                        attributes[name] = value.nodeValue;
208
                    }
209
                }
210
            }
211
            attrNode = attrNode.nextSibling;
212
        }
213
        return attributes;
214
    },
215

    
216
    /**
217
     * APIMethod: write
218
     * Accepts Feature Collection, and returns a string. 
219
     * 
220
     * Parameters: 
221
     * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.
222
     * metadata - {Object} A key/value pairs object to build a metadata node to
223
     *      add to the gpx. Supported keys are 'name', 'desc', 'author'.
224
     */
225
    write: function(features, metadata) {
226
        features = OpenLayers.Util.isArray(features) ?
227
            features : [features];
228
        var gpx = this.createElementNS(this.namespaces.gpx, "gpx");
229
        gpx.setAttribute("version", "1.1");
230
        gpx.setAttribute("creator", this.creator);
231
        this.setAttributes(gpx, {
232
            "xsi:schemaLocation": this.schemaLocation
233
        });
234

    
235
        if (metadata && typeof metadata == 'object') {
236
            gpx.appendChild(this.buildMetadataNode(metadata));
237
        }
238
        for(var i=0, len=features.length; i<len; i++) {
239
            gpx.appendChild(this.buildFeatureNode(features[i]));
240
        }
241
        return OpenLayers.Format.XML.prototype.write.apply(this, [gpx]);
242
    },
243

    
244
    /**
245
     * Method: buildMetadataNode
246
     * Creates a "metadata" node.
247
     *
248
     * Returns:
249
     * {DOMElement}
250
     */
251
    buildMetadataNode: function(metadata) {
252
        var types = ['name', 'desc', 'author'],
253
            node = this.createElementNS(this.namespaces.gpx, 'metadata');
254
        for (var i=0; i < types.length; i++) {
255
            var type = types[i];
256
            if (metadata[type]) {
257
                var n = this.createElementNS(this.namespaces.gpx, type);
258
                n.appendChild(this.createTextNode(metadata[type]));
259
                node.appendChild(n);
260
            }
261
        }
262
        return node;
263
    },
264

    
265
    /**
266
     * Method: buildFeatureNode
267
     * Accepts an <OpenLayers.Feature.Vector>, and builds a node for it.
268
     * 
269
     * Parameters:
270
     * feature - {<OpenLayers.Feature.Vector>}
271
     *
272
     * Returns:
273
     * {DOMElement} - The created node, either a 'wpt' or a 'trk'.
274
     */
275
    buildFeatureNode: function(feature) {
276
        var geometry = feature.geometry;
277
            geometry = geometry.clone();
278
        if (this.internalProjection && this.externalProjection) {
279
            geometry.transform(this.internalProjection, 
280
                               this.externalProjection);
281
        }
282
        if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
283
            var wpt = this.buildWptNode(geometry);
284
            this.appendAttributesNode(wpt, feature);
285
            return wpt;
286
        } else {
287
            var trkNode = this.createElementNS(this.namespaces.gpx, "trk");
288
            this.appendAttributesNode(trkNode, feature);
289
            var trkSegNodes = this.buildTrkSegNode(geometry);
290
            trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ?
291
                trkSegNodes : [trkSegNodes];
292
            for (var i = 0, len = trkSegNodes.length; i < len; i++) {
293
                trkNode.appendChild(trkSegNodes[i]);
294
            }
295
            return trkNode;
296
        }
297
    },
298

    
299
    /**
300
     * Method: buildTrkSegNode
301
     * Builds trkseg node(s) given a geometry
302
     *
303
     * Parameters:
304
     * trknode
305
     * geometry - {<OpenLayers.Geometry>}
306
     */
307
    buildTrkSegNode: function(geometry) {
308
        var node,
309
            i,
310
            len,
311
            point,
312
            nodes;
313
        if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
314
            geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
315
            node = this.createElementNS(this.namespaces.gpx, "trkseg");
316
            for (i = 0, len=geometry.components.length; i < len; i++) {
317
                point = geometry.components[i];
318
                node.appendChild(this.buildTrkPtNode(point));
319
            }
320
            return node;
321
        } else {
322
            nodes = [];
323
            for (i = 0, len = geometry.components.length; i < len; i++) {
324
                nodes.push(this.buildTrkSegNode(geometry.components[i]));
325
            }
326
            return nodes;
327
        }
328
    },
329
    
330
    /**
331
     * Method: buildTrkPtNode
332
     * Builds a trkpt node given a point 
333
     *
334
     * Parameters:
335
     * point - {<OpenLayers.Geometry.Point>}
336
     *
337
     * Returns:
338
     * {DOMElement} A trkpt node
339
     */
340
    buildTrkPtNode: function(point) {
341
        var node = this.createElementNS(this.namespaces.gpx, "trkpt");
342
        node.setAttribute("lon", point.x);
343
        node.setAttribute("lat", point.y);
344
        return node;
345
    },
346

    
347
    /**
348
     * Method: buildWptNode
349
     * Builds a wpt node given a point
350
     *
351
     * Parameters:
352
     * geometry - {<OpenLayers.Geometry.Point>}
353
     *
354
     * Returns:
355
     * {DOMElement} A wpt node
356
     */
357
    buildWptNode: function(geometry) {
358
        var node = this.createElementNS(this.namespaces.gpx, "wpt");
359
        node.setAttribute("lon", geometry.x);
360
        node.setAttribute("lat", geometry.y);
361
        return node;
362
    },
363

    
364
    /**
365
     * Method: appendAttributesNode
366
     * Adds some attributes node.
367
     *
368
     * Parameters:
369
     * node - {DOMElement} the node to append the attribute nodes to.
370
     * feature - {<OpenLayers.Feature.Vector>}
371
     */
372
    appendAttributesNode: function(node, feature) {
373
        var name = this.createElementNS(this.namespaces.gpx, 'name');
374
        name.appendChild(this.createTextNode(
375
            feature.attributes.name || feature.id));
376
        node.appendChild(name);
377
        var desc = this.createElementNS(this.namespaces.gpx, 'desc');
378
        desc.appendChild(this.createTextNode(
379
            feature.attributes.description || this.defaultDesc));
380
        node.appendChild(desc);
381
        // TBD - deal with remaining (non name/description) attributes.
382
    },
383

    
384
    CLASS_NAME: "OpenLayers.Format.GPX"
385
});
(10-10/41)