Adding gx:Track parsing to the KML format. If extractTracks is true, the parser will extract points from gx:Track elements as features, acquiring attributes from the Placemark plus when, trackId, altitude, heading, tilt, and roll (assuming angles are present). r=ahocevar (closes #2771)

git-svn-id: http://svn.openlayers.org/trunk/openlayers@10631 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
Tim Schaub
2010-08-18 15:38:09 +00:00
parent fc9e67318e
commit 415fcbf9f9
5 changed files with 3531 additions and 4 deletions

View File

@@ -24,6 +24,15 @@
*/
OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {
/**
* Property: namespaces
* {Object} Mapping of namespace aliases to namespace URIs.
*/
namespaces: {
kml: "http://www.opengis.net/kml/2.2",
gx: "http://www.google.com/kml/ext/2.2"
},
/**
* APIProperty: kmlns
* {String} KML Namespace to use. Defaults to 2.0 namespace.
@@ -65,6 +74,26 @@ OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {
*/
extractStyles: false,
/**
* APIProperty: extractTracks
* {Boolean} Extract gx:Track elements from Placemark elements. Default
* is false. If true, features will be generated for all points in
* all gx:Track elements. Features will have a when (Date) attribute
* based on when elements in the track. If tracks include angle
* elements, features will have heading, tilt, and roll attributes.
* If track point coordinates have three values, features will have
* an altitude attribute with the third coordinate value.
*/
extractTracks: false,
/**
* APIProperty: trackAttributes
* {Array} If <extractTracks> is true, points within gx:Track elements will
* be parsed as features with when, heading, tilt, and roll attributes.
* Any additional attribute names can be provided in <trackAttributes>.
*/
trackAttributes: null,
/**
* Property: internalns
* {String} KML Namespace to use -- defaults to the namespace of the
@@ -565,7 +594,7 @@ OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {
*
*/
parseFeatures: function(nodes, options) {
var features = new Array(nodes.length);
var features = [];
for(var i=0, len=nodes.length; i<len; i++) {
var featureNode = nodes[i];
var feature = this.parseFeature.apply(this,[featureNode]) ;
@@ -593,8 +622,26 @@ OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {
}
}
// add feature to list of features
features[i] = feature;
// check if gx:Track elements should be parsed
if (this.extractTracks) {
var tracks = this.getElementsByTagNameNS(
featureNode, this.namespaces.gx, "Track"
);
if (tracks && tracks.length > 0) {
var track = tracks[0];
var container = {
features: [],
feature: feature
};
this.readNode(track, container);
if (container.features.length > 0) {
features.push.apply(features, container.features);
}
}
} else {
// add feature to list of features
features.push(feature);
}
} else {
throw "Bad Placemark: " + i;
}
@@ -603,7 +650,100 @@ OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {
// add new features to existing feature list
this.features = this.features.concat(features);
},
/**
* Property: readers
* Contains public functions, grouped by namespace prefix, that will
* be applied when a namespaced node is found matching the function
* name. The function will be applied in the scope of this parser
* with two arguments: the node being read and a context object passed
* from the parent.
*/
readers: {
"kml": {
"when": function(node, container) {
container.whens.push(OpenLayers.Date.parse(
this.getChildValue(node)
));
},
"_trackPointAttribute": function(node, container) {
var name = node.nodeName.split(":").pop();
container.attributes[name].push(this.getChildValue(node));
}
},
"gx": {
"Track": function(node, container) {
var obj = {
whens: [],
points: [],
angles: []
};
if (this.trackAttributes) {
var name;
obj.attributes = {};
for (var i=0, ii=this.trackAttributes.length; i<ii; ++i) {
name = this.trackAttributes[i];
obj.attributes[name] = [];
if (!(name in this.readers.kml)) {
this.readers.kml[name] = this.readers.kml._trackPointAttribute;
}
}
}
this.readChildNodes(node, obj);
if (obj.whens.length !== obj.points.length) {
throw new Error("gx:Track with unequal number of when (" + obj.whens.length + ") and gx:coord (" + obj.points.length + ") elements.");
}
var hasAngles = obj.angles.length > 0;
if (hasAngles && obj.whens.length !== obj.angles.length) {
throw new Error("gx:Track with unequal number of when (" + obj.whens.length + ") and gx:angles (" + obj.angles.length + ") elements.");
}
var feature, point, angles;
for (var i=0, ii=obj.whens.length; i<ii; ++i) {
feature = container.feature.clone();
feature.fid = container.feature.fid || container.feature.id;
point = obj.points[i];
feature.geometry = point;
if ("z" in point) {
feature.attributes.altitude = point.z;
}
if (this.internalProjection && this.externalProjection) {
feature.geometry.transform(
this.externalProjection, this.internalProjection
);
}
if (this.trackAttributes) {
for (var j=0, jj=this.trackAttributes.length; j<jj; ++j) {
feature.attributes[name] = obj.attributes[this.trackAttributes[j]][i];
}
}
feature.attributes.when = obj.whens[i];
feature.attributes.trackId = container.feature.id;
if (hasAngles) {
angles = obj.angles[i];
feature.attributes.heading = parseFloat(angles[0]);
feature.attributes.tilt = parseFloat(angles[1]);
feature.attributes.roll = parseFloat(angles[2]);
}
container.features.push(feature);
}
},
"coord": function(node, container) {
var str = this.getChildValue(node);
var coords = str.replace(this.regExes.trimSpace, "").split(/\s+/);
var point = new OpenLayers.Geometry.Point(coords[0], coords[1]);
if (coords.length > 2) {
point.z = parseFloat(coords[2]);
}
container.points.push(point);
},
"angles": function(node, container) {
var str = this.getChildValue(node);
var parts = str.replace(this.regExes.trimSpace, "").split(/\s+/);
container.angles.push(parts);
}
}
},
/**
* Method: parseFeature
* This function is the core of the KML parsing code in OpenLayers.