Merge pull request #1716 from ahocevar/gpx-write
Write support for ol.format.GPX
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
|
||||
326
src/ol/xml.js
326
src/ol/xml.js
@@ -1,18 +1,81 @@
|
||||
// FIXME Remove ol.xml.makeParsersNS, and use ol.xml.makeStructureNS instead.
|
||||
|
||||
goog.provide('ol.xml');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom.NodeType');
|
||||
goog.require('goog.dom.xml');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* When using {@link ol.xml.makeChildAppender} or
|
||||
* {@link ol.xml.makeSimpleNodeFactory}, the top `objectStack` item needs to
|
||||
* have this structure.
|
||||
* @typedef {{node:Node}}
|
||||
*/
|
||||
ol.xml.NodeStackItem;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {function(Node, Array.<*>)}
|
||||
*/
|
||||
ol.xml.Parser;
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {function(Node, *, Array.<*>)}
|
||||
*/
|
||||
ol.xml.Serializer;
|
||||
|
||||
|
||||
/**
|
||||
* This document should be used when creating nodes for XML serializations. This
|
||||
* document is also used by {@link ol.xml.createElementNS} and
|
||||
* {@link ol.xml.setAttributeNS}
|
||||
* @const
|
||||
* @type {Document}
|
||||
*/
|
||||
ol.xml.DOCUMENT = goog.dom.xml.createDocument();
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} namespaceURI Namespace URI.
|
||||
* @param {string} qualifiedName Qualified name.
|
||||
* @return {Node} Node.
|
||||
* @private
|
||||
*/
|
||||
ol.xml.createElementNS_ = function(namespaceURI, qualifiedName) {
|
||||
return ol.xml.DOCUMENT.createElementNS(namespaceURI, qualifiedName);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} namespaceURI Namespace URI.
|
||||
* @param {string} qualifiedName Qualified name.
|
||||
* @return {Node} Node.
|
||||
* @private
|
||||
*/
|
||||
ol.xml.createElementNSActiveX_ = function(namespaceURI, qualifiedName) {
|
||||
if (goog.isNull(namespaceURI)) {
|
||||
namespaceURI = '';
|
||||
}
|
||||
return ol.xml.DOCUMENT.createNode(1, qualifiedName, namespaceURI);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} namespaceURI Namespace URI.
|
||||
* @param {string} qualifiedName Qualified name.
|
||||
* @return {Node} Node.
|
||||
*/
|
||||
ol.xml.createElementNS =
|
||||
(document.implementation && document.implementation.createDocument) ?
|
||||
ol.xml.createElementNS_ : ol.xml.createElementNSActiveX_;
|
||||
|
||||
|
||||
/**
|
||||
* @param {Node} node Node.
|
||||
* @param {boolean} normalizeWhitespace Normalize whitespace.
|
||||
@@ -138,6 +201,47 @@ ol.xml.isNodeIE_ = function(value) {
|
||||
ol.xml.isNode = goog.userAgent.IE ? ol.xml.isNodeIE_ : ol.xml.isNode_;
|
||||
|
||||
|
||||
/**
|
||||
* @param {Node} node Node.
|
||||
* @param {?string} namespaceURI Namespace URI.
|
||||
* @param {string} name Attribute name.
|
||||
* @param {string|number} value Value.
|
||||
* @private
|
||||
*/
|
||||
ol.xml.setAttributeNS_ = function(node, namespaceURI, name, value) {
|
||||
node.setAttributeNS(namespaceURI, name, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Node} node Node.
|
||||
* @param {?string} namespaceURI Namespace URI.
|
||||
* @param {string} name Attribute name.
|
||||
* @param {string|number} value Value.
|
||||
* @private
|
||||
*/
|
||||
ol.xml.setAttributeNSActiveX_ = function(node, namespaceURI, name, value) {
|
||||
if (!goog.isNull(namespaceURI)) {
|
||||
var attribute = node.ownerDocument.createNode(2, name, namespaceURI);
|
||||
attribute.nodeValue = value;
|
||||
node.setAttributeNode(attribute);
|
||||
} else {
|
||||
node.setAttribute(name, value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Node} node Node.
|
||||
* @param {?string} namespaceURI Namespace URI.
|
||||
* @param {string} name Attribute name.
|
||||
* @param {string|number} value Value.
|
||||
*/
|
||||
ol.xml.setAttributeNS =
|
||||
(document.implementation && document.implementation.createDocument) ?
|
||||
ol.xml.setAttributeNS_ : ol.xml.setAttributeNSActiveX_;
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} xml XML.
|
||||
* @return {Document} Document.
|
||||
@@ -254,13 +358,155 @@ ol.xml.makeObjectPropertySetter =
|
||||
* @return {Object.<string, Object.<string, ol.xml.Parser>>} Parsers NS.
|
||||
*/
|
||||
ol.xml.makeParsersNS = function(namespaceURIs, parsers, opt_parsersNS) {
|
||||
/** @type {Object.<string, Object.<string, ol.xml.Parser>>} */
|
||||
var parsersNS = goog.isDef(opt_parsersNS) ? opt_parsersNS : {};
|
||||
return /** @type {Object.<string, Object.<string, ol.xml.Parser>>} */ (
|
||||
ol.xml.makeStructureNS(namespaceURIs, parsers, opt_parsersNS));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a serializer that appends nodes written by its `nodeWriter` to its
|
||||
* designated parent. The parent is the `node` of the
|
||||
* {@link ol.xml.NodeStackItem} at the top of the `objectStack`.
|
||||
* @param {function(this: T, Node, V, Array.<*>)}
|
||||
* nodeWriter Node writer.
|
||||
* @param {T=} opt_this The object to use as `this` in `nodeWriter`.
|
||||
* @return {ol.xml.Serializer} Serializer.
|
||||
* @template T, V
|
||||
*/
|
||||
ol.xml.makeChildAppender = function(nodeWriter, opt_this) {
|
||||
return function(node, value, objectStack) {
|
||||
nodeWriter.call(opt_this, node, value, objectStack);
|
||||
var parent = objectStack[objectStack.length - 1];
|
||||
goog.asserts.assert(goog.isObject(parent));
|
||||
var parentNode = parent.node;
|
||||
goog.asserts.assert(ol.xml.isNode(parentNode) ||
|
||||
ol.xml.isDocument(parentNode));
|
||||
parentNode.appendChild(node);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a serializer that calls the provided `nodeWriter` from
|
||||
* {@link ol.xml.serialize}. This can be used by the parent writer to have the
|
||||
* 'nodeWriter' called with an array of values when the `nodeWriter` was
|
||||
* designed to serialize a single item. An example would be a LineString
|
||||
* geometry writer, which could be reused for writing MultiLineString
|
||||
* geometries.
|
||||
* @param {function(this: T, Node, V, Array.<*>)}
|
||||
* nodeWriter Node writer.
|
||||
* @param {T=} opt_this The object to use as `this` in `nodeWriter`.
|
||||
* @return {ol.xml.Serializer} Serializer.
|
||||
* @template T, V
|
||||
*/
|
||||
ol.xml.makeArraySerializer = function(nodeWriter, opt_this) {
|
||||
var serializersNS, nodeFactory;
|
||||
return function(node, value, objectStack) {
|
||||
if (!goog.isDef(serializersNS)) {
|
||||
serializersNS = {};
|
||||
var serializers = {};
|
||||
goog.object.set(serializers, node.localName, nodeWriter);
|
||||
goog.object.set(serializersNS, node.namespaceURI, serializers);
|
||||
nodeFactory = ol.xml.makeSimpleNodeFactory(node.localName);
|
||||
}
|
||||
ol.xml.serialize(serializersNS, nodeFactory, value, objectStack);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a node factory which can use the `opt_keys` passed to
|
||||
* {@link ol.xml.serialize} or {@link ol.xml.pushSerializeAndPop} as node names,
|
||||
* or a fixed node name. The namespace of the created nodes can either be fixed,
|
||||
* or the parent namespace will be used.
|
||||
* @param {string=} opt_nodeName Fixed node name which will be used for all
|
||||
* created nodes. If not provided, the 3rd argument to the resulting node
|
||||
* factory needs to be provided and will be the nodeName.
|
||||
* @param {string=} opt_namespaceURI Fixed namespace URI which will be used for
|
||||
* all created nodes. If not provided, the namespace of the parent node will
|
||||
* be used.
|
||||
* @return {function(*, Array.<*>, string=): (Node|undefined)} Node factory.
|
||||
*/
|
||||
ol.xml.makeSimpleNodeFactory = function(opt_nodeName, opt_namespaceURI) {
|
||||
var fixedNodeName = opt_nodeName;
|
||||
return (
|
||||
/**
|
||||
* @param {*} value Value.
|
||||
* @param {Array.<*>} objectStack Object stack.
|
||||
* @param {string=} opt_nodeName Node name.
|
||||
* @return {Node} Node.
|
||||
*/
|
||||
function(value, objectStack, opt_nodeName) {
|
||||
var context = objectStack[objectStack.length - 1];
|
||||
var node = context.node;
|
||||
goog.asserts.assert(ol.xml.isNode(node) || ol.xml.isDocument(node));
|
||||
var nodeName = fixedNodeName;
|
||||
if (!goog.isDef(nodeName)) {
|
||||
nodeName = opt_nodeName;
|
||||
}
|
||||
var namespaceURI = opt_namespaceURI;
|
||||
if (!goog.isDef(opt_namespaceURI)) {
|
||||
namespaceURI = node.namespaceURI;
|
||||
}
|
||||
goog.asserts.assert(goog.isDef(nodeName));
|
||||
return ol.xml.createElementNS(namespaceURI, nodeName);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A node factory that creates a node using the parent's `namespaceURI` and the
|
||||
* `nodeName` passed by {@link ol.xml.serialize} or
|
||||
* {@link ol.xml.pushSerializeAndPop} to the node factory.
|
||||
* @const
|
||||
* @type {function(*, Array.<*>, string=): (Node|undefined)}
|
||||
*/
|
||||
ol.xml.OBJECT_PROPERTY_NODE_FACTORY = ol.xml.makeSimpleNodeFactory();
|
||||
|
||||
|
||||
/**
|
||||
* Creates an array of `values` to be used with {@link ol.xml.serialize} or
|
||||
* {@link ol.xml.pushSerializeAndPop}, where `orderedKeys` has to be provided as
|
||||
* `opt_key` argument.
|
||||
* @param {Object.<string, V>} object Key-value pairs for the sequence. Keys can
|
||||
* be a subset of the `orderedKeys`.
|
||||
* @param {Array.<string>} orderedKeys Keys in the order of the sequence.
|
||||
* @return {Array.<V>} Values in the order of the sequence. The resulting array
|
||||
* has the same length as the `orderedKeys` array. Values that are not
|
||||
* present in `object` will be `undefined` in the resulting array.
|
||||
* @template V
|
||||
*/
|
||||
ol.xml.makeSequence = function(object, orderedKeys) {
|
||||
var length = orderedKeys.length;
|
||||
var sequence = new Array(length);
|
||||
for (var i = 0; i < length; ++i) {
|
||||
sequence[i] = object[orderedKeys[i]];
|
||||
}
|
||||
return sequence;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a namespaced structure, using the same values for each namespace.
|
||||
* This can be used as a starting point for versioned parsers, when only a few
|
||||
* values are version specific.
|
||||
* @param {Array.<string>} namespaceURIs Namespace URIs.
|
||||
* @param {T} structure Structure.
|
||||
* @param {Object.<string, T>=} opt_structureNS Namespaced structure to add to.
|
||||
* @return {Object.<string, T>} Namespaced structure.
|
||||
* @template T
|
||||
*/
|
||||
ol.xml.makeStructureNS = function(namespaceURIs, structure, opt_structureNS) {
|
||||
/**
|
||||
* @type {Object.<string, *>}
|
||||
*/
|
||||
var structureNS = goog.isDef(opt_structureNS) ? opt_structureNS : {};
|
||||
var i, ii;
|
||||
for (i = 0, ii = namespaceURIs.length; i < ii; ++i) {
|
||||
parsersNS[namespaceURIs[i]] = parsers;
|
||||
structureNS[namespaceURIs[i]] = structure;
|
||||
}
|
||||
return parsersNS;
|
||||
return structureNS;
|
||||
};
|
||||
|
||||
|
||||
@@ -301,3 +547,75 @@ ol.xml.pushParseAndPop = function(
|
||||
ol.xml.parse(parsersNS, node, objectStack, opt_this);
|
||||
return objectStack.pop();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Walks through an array of `values` and calls a serializer for each value.
|
||||
* @param {Object.<string, Object.<string, ol.xml.Serializer>>} serializersNS
|
||||
* Namespaced serializers.
|
||||
* @param {function(this: T, *, Array.<*>, (string|undefined)): (Node|undefined)} nodeFactory
|
||||
* Node factory. The `nodeFactory` creates the node whose namespace and name
|
||||
* will be used to choose a node writer from `serializersNS`. This
|
||||
* separation allows us to decide what kind of node to create, depending on
|
||||
* the value we want to serialize. An example for this would be different
|
||||
* geometry writers based on the geometry type.
|
||||
* @param {Array.<*>} values Values to serialize. An example would be an array
|
||||
* of {@link ol.Feature} instances.
|
||||
* @param {Array.<*>} objectStack Node stack.
|
||||
* @param {Array.<string>=} opt_keys Keys of the `values`. Will be passed to the
|
||||
* `nodeFactory`. This is used for serializing object literals where the
|
||||
* node name relates to the property key. The array length of `opt_keys` has
|
||||
* to match the length of `values`. For serializing a sequence, `opt_keys`
|
||||
* determines the order of the sequence.
|
||||
* @param {T=} opt_this The object to use as `this` for the node factory and
|
||||
* serializers.
|
||||
* @template T
|
||||
*/
|
||||
ol.xml.serialize = function(
|
||||
serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this) {
|
||||
var length = (goog.isDef(opt_keys) ? opt_keys : values).length;
|
||||
var value, node;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
value = values[i];
|
||||
if (goog.isDef(value)) {
|
||||
node = nodeFactory.call(opt_this, value, objectStack,
|
||||
goog.isDef(opt_keys) ? opt_keys[i] : undefined);
|
||||
if (goog.isDef(node)) {
|
||||
serializersNS[node.namespaceURI][node.localName]
|
||||
.call(opt_this, node, value, objectStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {O} object Object.
|
||||
* @param {Object.<string, Object.<string, ol.xml.Serializer>>} serializersNS
|
||||
* Namespaced serializers.
|
||||
* @param {function(this: T, *, Array.<*>, (string|undefined)): (Node|undefined)} nodeFactory
|
||||
* Node factory. The `nodeFactory` creates the node whose namespace and name
|
||||
* will be used to choose a node writer from `serializersNS`. This
|
||||
* separation allows us to decide what kind of node to create, depending on
|
||||
* the value we want to serialize. An example for this would be different
|
||||
* geometry writers based on the geometry type.
|
||||
* @param {Array.<*>} values Values to serialize. An example would be an array
|
||||
* of {@link ol.Feature} instances.
|
||||
* @param {Array.<*>} objectStack Node stack.
|
||||
* @param {Array.<string>=} opt_keys Keys of the `values`. Will be passed to the
|
||||
* `nodeFactory`. This is used for serializing object literals where the
|
||||
* node name relates to the property key. The array length of `opt_keys` has
|
||||
* to match the length of `values`. For serializing a sequence, `opt_keys`
|
||||
* determines the order of the sequence.
|
||||
* @param {T=} opt_this The object to use as `this` for the node factory and
|
||||
* serializers.
|
||||
* @return {O|undefined} Object.
|
||||
* @template O, T
|
||||
*/
|
||||
ol.xml.pushSerializeAndPop = function(object,
|
||||
serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this) {
|
||||
objectStack.push(object);
|
||||
ol.xml.serialize(
|
||||
serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this);
|
||||
return objectStack.pop();
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ describe('ol.format.GPX', function() {
|
||||
|
||||
var format;
|
||||
beforeEach(function() {
|
||||
format = new ol.format.GPX();
|
||||
format = new ol.format.GPX.V1_1();
|
||||
});
|
||||
|
||||
describe('readFeatures', function() {
|
||||
@@ -27,7 +27,7 @@ describe('ol.format.GPX', function() {
|
||||
expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
|
||||
});
|
||||
|
||||
it('can read various rte attributes', function() {
|
||||
it('can read and write various rte attributes', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <rte>' +
|
||||
@@ -56,9 +56,11 @@ describe('ol.format.GPX', function() {
|
||||
expect(f.get('linkType')).to.be('Link type');
|
||||
expect(f.get('number')).to.be(1);
|
||||
expect(f.get('type')).to.be('Type');
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
it('can read a rte with multiple rtepts', function() {
|
||||
it('can read and write a rte with multiple rtepts', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <rte>' +
|
||||
@@ -74,6 +76,8 @@ describe('ol.format.GPX', function() {
|
||||
expect(g).to.be.an(ol.geom.LineString);
|
||||
expect(g.getCoordinates()).to.eql([[2, 1, 0, 0], [4, 3, 0, 0]]);
|
||||
expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
});
|
||||
@@ -95,7 +99,7 @@ describe('ol.format.GPX', function() {
|
||||
expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
|
||||
});
|
||||
|
||||
it('can read various trk attributes', function() {
|
||||
it('can read and write various trk attributes', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <trk>' +
|
||||
@@ -124,9 +128,11 @@ describe('ol.format.GPX', function() {
|
||||
expect(f.get('linkType')).to.be('Link type');
|
||||
expect(f.get('number')).to.be(1);
|
||||
expect(f.get('type')).to.be('Type');
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
it('can read a trk with an empty trkseg', function() {
|
||||
it('can read and write a trk with an empty trkseg', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <trk>' +
|
||||
@@ -141,9 +147,11 @@ describe('ol.format.GPX', function() {
|
||||
expect(g).to.be.an(ol.geom.MultiLineString);
|
||||
expect(g.getCoordinates()).to.eql([[]]);
|
||||
expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
it('can read a trk with a trkseg with multiple trkpts', function() {
|
||||
it('can read/write a trk with a trkseg with multiple trkpts', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <trk>' +
|
||||
@@ -169,9 +177,11 @@ describe('ol.format.GPX', function() {
|
||||
[[2, 1, 3, 1263115752], [6, 5, 7, 1263115812]]
|
||||
]);
|
||||
expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
it('can read a trk with multiple trksegs', function() {
|
||||
it('can read and write a trk with multiple trksegs', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <trk>' +
|
||||
@@ -208,13 +218,15 @@ describe('ol.format.GPX', function() {
|
||||
[[9, 8, 10, 1263115872], [12, 11, 13, 1263115932]]
|
||||
]);
|
||||
expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('wpt', function() {
|
||||
|
||||
it('can read a wpt', function() {
|
||||
it('can read and write a wpt', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <wpt lat="1" lon="2"/>' +
|
||||
@@ -227,9 +239,11 @@ describe('ol.format.GPX', function() {
|
||||
expect(g).to.be.an(ol.geom.Point);
|
||||
expect(g.getCoordinates()).to.eql([2, 1, 0, 0]);
|
||||
expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
it('can read a wpt with ele', function() {
|
||||
it('can read and write a wpt with ele', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <wpt lat="1" lon="2">' +
|
||||
@@ -244,9 +258,11 @@ describe('ol.format.GPX', function() {
|
||||
expect(g).to.be.an(ol.geom.Point);
|
||||
expect(g.getCoordinates()).to.eql([2, 1, 3, 0]);
|
||||
expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
it('can read a wpt with time', function() {
|
||||
it('can read and write a wpt with time', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <wpt lat="1" lon="2">' +
|
||||
@@ -261,9 +277,11 @@ describe('ol.format.GPX', function() {
|
||||
expect(g).to.be.an(ol.geom.Point);
|
||||
expect(g.getCoordinates()).to.eql([2, 1, 0, 1263115752]);
|
||||
expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
it('can read a wpt with ele and time', function() {
|
||||
it('can read and write a wpt with ele and time', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <wpt lat="1" lon="2">' +
|
||||
@@ -279,9 +297,11 @@ describe('ol.format.GPX', function() {
|
||||
expect(g).to.be.an(ol.geom.Point);
|
||||
expect(g.getCoordinates()).to.eql([2, 1, 3, 1263115752]);
|
||||
expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
it('can read various wpt attributes', function() {
|
||||
it('can read and write various wpt attributes', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/1">' +
|
||||
' <wpt lat="1" lon="2">' +
|
||||
@@ -327,12 +347,18 @@ describe('ol.format.GPX', function() {
|
||||
expect(f.get('pdop')).to.be(8);
|
||||
expect(f.get('ageofdgpsdata')).to.be(9);
|
||||
expect(f.get('dgpsid')).to.be(10);
|
||||
var serialized = format.writeFeatures(fs);
|
||||
expect(serialized).to.xmleql(ol.xml.load(text));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('XML namespace support', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
format = new ol.format.GPX();
|
||||
});
|
||||
|
||||
it('can read features with a version 1.0 namespace', function() {
|
||||
var text =
|
||||
'<gpx xmlns="http://www.topografix.com/GPX/1/0">' +
|
||||
@@ -375,6 +401,8 @@ describe('ol.format.GPX', function() {
|
||||
|
||||
goog.require('ol.Feature');
|
||||
goog.require('ol.format.GPX');
|
||||
goog.require('ol.format.GPX.V1_1');
|
||||
goog.require('ol.geom.LineString');
|
||||
goog.require('ol.geom.MultiLineString');
|
||||
goog.require('ol.geom.Point');
|
||||
goog.require('ol.xml');
|
||||
|
||||
Reference in New Issue
Block a user