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
|
});
|