Merge pull request #1716 from ahocevar/gpx-write

Write support for ol.format.GPX
This commit is contained in:
ahocevar
2014-02-24 14:23:16 +01:00
4 changed files with 772 additions and 16 deletions

View File

@@ -1,4 +1,5 @@
goog.provide('ol.format.GPX');
goog.provide('ol.format.GPX.V1_1');
goog.require('goog.array');
goog.require('goog.asserts');
@@ -420,3 +421,363 @@ ol.format.GPX.prototype.readProjectionFromDocument = function(doc) {
ol.format.GPX.prototype.readProjectionFromNode = function(node) {
return ol.proj.get('EPSG:4326');
};
/**
* @param {Node} node Node.
* @param {string} value Value for the link's `href` attribute.
* @param {Array.<*>} objectStack Node stack.
* @private
*/
ol.format.GPX.writeLink_ = function(node, value, objectStack) {
node.setAttribute('href', value);
var context = objectStack[objectStack.length - 1];
goog.asserts.assert(goog.isObject(context));
var properties = goog.object.get(context, 'properties');
var link = [
goog.object.get(properties, 'linkText'),
goog.object.get(properties, 'linkType')
];
ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */ ({node: node}),
ol.format.GPX.LINK_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
link, objectStack, ol.format.GPX.LINK_SEQUENCE_);
};
/**
* @param {Node} node Node.
* @param {ol.Coordinate} coordinate Coordinate.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
ol.format.GPX.writeWptType_ = function(node, coordinate, objectStack) {
var context = objectStack[objectStack.length - 1];
goog.asserts.assert(goog.isObject(context));
var parentNode = context.node;
goog.asserts.assert(ol.xml.isNode(parentNode));
var namespaceURI = parentNode.namespaceURI;
var properties = goog.object.get(context, 'properties');
//FIXME Projection handling
ol.xml.setAttributeNS(node, null, 'lat', coordinate[1]);
ol.xml.setAttributeNS(node, null, 'lon', coordinate[0]);
var geometryLayout = goog.object.get(context, 'geometryLayout');
switch (geometryLayout) {
case ol.geom.GeometryLayout.XYZM:
if (coordinate[3] !== 0) {
goog.object.set(properties, 'time', coordinate[3]);
}
case ol.geom.GeometryLayout.XYZ:
if (coordinate[2] !== 0) {
goog.object.set(properties, 'ele', coordinate[2]);
}
break;
case ol.geom.GeometryLayout.XYM:
if (coordinate[2] !== 0) {
goog.object.set(properties, 'time', coordinate[2]);
}
}
var orderedKeys = ol.format.GPX.WPT_TYPE_SEQUENCE_[namespaceURI];
var values = ol.xml.makeSequence(properties, orderedKeys);
ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */
({node: node, 'properties': properties}),
ol.format.GPX.WPT_TYPE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
values, objectStack, orderedKeys);
};
/**
* @param {Node} node Node.
* @param {ol.Feature} feature Feature.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
ol.format.GPX.writeRte_ = function(node, feature, objectStack) {
var properties = feature.getProperties();
var context = {node: node, 'properties': properties};
var geometry = feature.getGeometry();
if (goog.isDef(geometry)) {
goog.asserts.assertInstanceof(geometry, ol.geom.LineString);
goog.object.set(context, 'geometryLayout', geometry.getLayout());
goog.object.set(properties, 'rtept', geometry.getCoordinates());
}
var parentNode = objectStack[objectStack.length - 1].node;
var orderedKeys = ol.format.GPX.RTE_SEQUENCE_[parentNode.namespaceURI];
var values = ol.xml.makeSequence(properties, orderedKeys);
ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */ (context),
ol.format.GPX.RTE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
values, objectStack, orderedKeys);
};
/**
* @param {Node} node Node.
* @param {ol.Feature} feature Feature.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
ol.format.GPX.writeTrk_ = function(node, feature, objectStack) {
var properties = feature.getProperties();
var context = {node: node, 'properties': properties};
var geometry = feature.getGeometry();
if (goog.isDef(geometry)) {
goog.asserts.assertInstanceof(geometry, ol.geom.MultiLineString);
goog.object.set(properties, 'trkseg', geometry.getLineStrings());
}
var parentNode = objectStack[objectStack.length - 1].node;
var orderedKeys = ol.format.GPX.TRK_SEQUENCE_[parentNode.namespaceURI];
var values = ol.xml.makeSequence(properties, orderedKeys);
ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */ (context),
ol.format.GPX.TRK_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY,
values, objectStack, orderedKeys);
};
/**
* @param {Node} node Node.
* @param {ol.geom.LineString} lineString LineString.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
ol.format.GPX.writeTrkSeg_ = function(node, lineString, objectStack) {
var context = {node: node, 'geometryLayout': lineString.getLayout(),
'properties': {}};
ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */ (context),
ol.format.GPX.TRKSEG_SERIALIZERS_, ol.format.GPX.TRKSEG_NODE_FACTORY_,
lineString.getCoordinates(), objectStack);
};
/**
* @param {Node} node Node.
* @param {ol.Feature} feature Feature.
* @param {Array.<*>} objectStack Object stack.
* @private
*/
ol.format.GPX.writeWpt_ = function(node, feature, objectStack) {
var context = objectStack[objectStack.length - 1];
goog.asserts.assert(goog.isObject(context));
goog.object.set(context, 'properties', feature.getProperties());
var geometry = feature.getGeometry();
if (goog.isDef(geometry)) {
goog.asserts.assertInstanceof(geometry, ol.geom.Point);
goog.object.set(context, 'geometryLayout', geometry.getLayout());
ol.format.GPX.writeWptType_(node, geometry.getCoordinates(), objectStack);
}
};
/**
* @const
* @type {Array.<string>}
* @private
*/
ol.format.GPX.LINK_SEQUENCE_ = ['text', 'type'];
/**
* @type {Object.<string, Object.<string, ol.xml.Serializer>>}
* @private
*/
ol.format.GPX.LINK_SERIALIZERS_ = ol.xml.makeStructureNS(
ol.format.GPX.NAMESPACE_URIS_, {
'text': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode)
});
/**
* @const
* @type {Object.<string, Array.<string>>}
* @private
*/
ol.format.GPX.RTE_SEQUENCE_ = ol.xml.makeStructureNS(
ol.format.GPX.NAMESPACE_URIS_, [
'name', 'cmt', 'desc', 'src', 'link', 'number', 'type', 'rtept'
]);
/**
* @const
* @type {Object.<string, Object.<string, ol.xml.Serializer>>}
* @private
*/
ol.format.GPX.RTE_SERIALIZERS_ = ol.xml.makeStructureNS(
ol.format.GPX.NAMESPACE_URIS_, {
'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_),
'number': ol.xml.makeChildAppender(
ol.format.XSD.writeNonNegativeIntegerTextNode),
'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'rtept': ol.xml.makeArraySerializer(ol.xml.makeChildAppender(
ol.format.GPX.writeWptType_))
});
/**
* @const
* @type {Object.<string, Array.<string>>}
* @private
*/
ol.format.GPX.TRK_SEQUENCE_ = ol.xml.makeStructureNS(
ol.format.GPX.NAMESPACE_URIS_, [
'name', 'cmt', 'desc', 'src', 'link', 'number', 'type', 'trkseg'
]);
/**
* @const
* @type {Object.<string, Object.<string, ol.xml.Serializer>>}
* @private
*/
ol.format.GPX.TRK_SERIALIZERS_ = ol.xml.makeStructureNS(
ol.format.GPX.NAMESPACE_URIS_, {
'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_),
'number': ol.xml.makeChildAppender(
ol.format.XSD.writeNonNegativeIntegerTextNode),
'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'trkseg': ol.xml.makeArraySerializer(ol.xml.makeChildAppender(
ol.format.GPX.writeTrkSeg_))
});
/**
* @const
* @param {*} value Value.
* @param {Array.<*>} objectStack Object stack.
* @param {string=} opt_nodeName Node name.
* @return {Node|undefined} Node.
* @private
*/
ol.format.GPX.TRKSEG_NODE_FACTORY_ = ol.xml.makeSimpleNodeFactory('trkpt');
/**
* @const
* @type {Object.<string, Object.<string, ol.xml.Serializer>>}
* @private
*/
ol.format.GPX.TRKSEG_SERIALIZERS_ = ol.xml.makeStructureNS(
ol.format.GPX.NAMESPACE_URIS_, {
'trkpt': ol.xml.makeChildAppender(ol.format.GPX.writeWptType_)
});
/**
* @const
* @type {Object.<string, Array.<string>>}
* @private
*/
ol.format.GPX.WPT_TYPE_SEQUENCE_ = ol.xml.makeStructureNS(
ol.format.GPX.NAMESPACE_URIS_, [
'ele', 'time', 'magvar', 'geoidheight', 'name', 'cmt', 'desc', 'src',
'link', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop', 'pdop',
'ageofdgpsdata', 'dgpsid'
]);
/**
* @type {Object.<string, Object.<string, ol.xml.Serializer>>}
* @private
*/
ol.format.GPX.WPT_TYPE_SERIALIZERS_ = ol.xml.makeStructureNS(
ol.format.GPX.NAMESPACE_URIS_, {
'ele': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
'time': ol.xml.makeChildAppender(ol.format.XSD.writeDateTimeTextNode),
'magvar': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
'geoidheight': ol.xml.makeChildAppender(
ol.format.XSD.writeDecimalTextNode),
'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_),
'sym': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'fix': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode),
'sat': ol.xml.makeChildAppender(
ol.format.XSD.writeNonNegativeIntegerTextNode),
'hdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
'vdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
'pdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode),
'ageofdgpsdata': ol.xml.makeChildAppender(
ol.format.XSD.writeDecimalTextNode),
'dgpsid': ol.xml.makeChildAppender(
ol.format.XSD.writeNonNegativeIntegerTextNode)
});
/**
* @const
* @type {Object.<string, string>}
* @private
*/
ol.format.GPX.GEOMETRY_TYPE_TO_NODENAME_ = {
'Point': 'wpt',
'LineString': 'rte',
'MultiLineString': 'trk'
};
/**
* @const
* @param {*} value Value.
* @param {Array.<*>} objectStack Object stack.
* @param {string=} opt_nodeName Node name.
* @return {Node|undefined} Node.
* @private
*/
ol.format.GPX.GPX_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) {
goog.asserts.assertInstanceof(value, ol.Feature);
var geometry = value.getGeometry();
if (goog.isDef(geometry)) {
var parentNode = objectStack[objectStack.length - 1].node;
goog.asserts.assert(ol.xml.isNode(parentNode));
return ol.xml.createElementNS(parentNode.namespaceURI,
ol.format.GPX.GEOMETRY_TYPE_TO_NODENAME_[geometry.getType()]);
}
};
/**
* @const
* @type {Object.<string, Object.<string, ol.xml.Serializer>>}
* @private
*/
ol.format.GPX.GPX_SERIALIZERS_ = ol.xml.makeStructureNS(
ol.format.GPX.NAMESPACE_URIS_, {
'rte': ol.xml.makeChildAppender(ol.format.GPX.writeRte_),
'trk': ol.xml.makeChildAppender(ol.format.GPX.writeTrk_),
'wpt': ol.xml.makeChildAppender(ol.format.GPX.writeWpt_)
});
/**
* @constructor
* @extends {ol.format.GPX}
* @todo stability experimental
*/
ol.format.GPX.V1_1 = function() {
goog.base(this);
};
goog.inherits(ol.format.GPX.V1_1, ol.format.GPX);
/**
* @inheritDoc
*/
ol.format.GPX.V1_1.prototype.writeFeaturesNode = function(features) {
//FIXME Serialize metadata
var gpx = ol.xml.createElementNS('http://www.topografix.com/GPX/1/1', 'gpx');
ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */
({node: gpx}), ol.format.GPX.GPX_SERIALIZERS_,
ol.format.GPX.GPX_NODE_FACTORY_, features, []);
return gpx;
};

View File

@@ -1,5 +1,6 @@
goog.provide('ol.format.XSD');
goog.require('goog.asserts');
goog.require('goog.string');
goog.require('ol.xml');
@@ -123,3 +124,51 @@ ol.format.XSD.readString = function(node) {
var s = ol.xml.getAllTextContent(node, false);
return goog.string.trim(s);
};
/**
* @param {Node} node Node to append a TextNode with the dateTime to.
* @param {number} dateTime DateTime in seconds.
*/
ol.format.XSD.writeDateTimeTextNode = function(node, dateTime) {
var date = new Date(dateTime * 1000);
var string = date.getUTCFullYear() + '-' +
goog.string.padNumber(date.getUTCMonth() + 1, 2) + '-' +
goog.string.padNumber(date.getUTCDate(), 2) + 'T' +
goog.string.padNumber(date.getUTCHours(), 2) + ':' +
goog.string.padNumber(date.getUTCMinutes(), 2) + ':' +
goog.string.padNumber(date.getUTCSeconds(), 2) + 'Z';
node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
};
/**
* @param {Node} node Node to append a TextNode with the decimal to.
* @param {number} decimal Decimal.
*/
ol.format.XSD.writeDecimalTextNode = function(node, decimal) {
var string = decimal.toPrecision();
node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
};
/**
* @param {Node} node Node to append a TextNode with the decimal to.
* @param {number} nonNegativeInteger Non negative integer.
*/
ol.format.XSD.writeNonNegativeIntegerTextNode =
function(node, nonNegativeInteger) {
goog.asserts.assert(nonNegativeInteger >= 0);
goog.asserts.assert(nonNegativeInteger == (nonNegativeInteger | 0));
var string = nonNegativeInteger.toString();
node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
};
/**
* @param {Node} node Node to append a TextNode with the string to.
* @param {string} string String.
*/
ol.format.XSD.writeStringTextNode = function(node, string) {
node.appendChild(ol.xml.DOCUMENT.createTextNode(string));
};