diff --git a/examples/drag-and-drop.js b/examples/drag-and-drop.js
index dc79f50a73..ce8d40780d 100644
--- a/examples/drag-and-drop.js
+++ b/examples/drag-and-drop.js
@@ -1,19 +1,98 @@
goog.require('ol.Map');
goog.require('ol.RendererHint');
goog.require('ol.View2D');
+goog.require('ol.format.GPX');
+goog.require('ol.format.GeoJSON');
+goog.require('ol.format.IGC');
goog.require('ol.format.KML');
+goog.require('ol.format.TopoJSON');
goog.require('ol.interaction');
goog.require('ol.interaction.DragAndDrop');
goog.require('ol.layer.Tile');
goog.require('ol.source.BingMaps');
+goog.require('ol.style.Circle');
+goog.require('ol.style.Fill');
+goog.require('ol.style.Stroke');
+goog.require('ol.style.Style');
+var defaultStyle = {
+ 'Point': [new ol.style.Style({
+ image: new ol.style.Circle({
+ fill: new ol.style.Fill({
+ color: 'rgba(255,255,0,0.5)'
+ }),
+ radius: 5,
+ stroke: new ol.style.Stroke({
+ color: '#ff0',
+ width: 1
+ })
+ })
+ })],
+ 'LineString': [new ol.style.Style({
+ stroke: new ol.style.Stroke({
+ color: '#f00',
+ width: 3
+ })
+ })],
+ 'Polygon': [new ol.style.Style({
+ fill: new ol.style.Fill({
+ color: 'rgba(0,255,255,0.5)'
+ }),
+ stroke: new ol.style.Stroke({
+ color: '#0ff',
+ width: 1
+ })
+ })],
+ 'MultiPoint': [new ol.style.Style({
+ image: new ol.style.Circle({
+ fill: new ol.style.Fill({
+ color: 'rgba(255,0,255,0.5)'
+ }),
+ radius: 5,
+ stroke: new ol.style.Stroke({
+ color: '#f0f',
+ width: 1
+ })
+ })
+ })],
+ 'MultiLineString': [new ol.style.Style({
+ stroke: new ol.style.Stroke({
+ color: '#0f0',
+ width: 3
+ })
+ })],
+ 'MultiPolygon': [new ol.style.Style({
+ fill: new ol.style.Fill({
+ color: 'rgba(0,0,255,0.5)'
+ }),
+ stroke: new ol.style.Stroke({
+ color: '#00f',
+ width: 1
+ })
+ })]
+};
+
+var styleFunction = function(feature, resolution) {
+ var featureStyleFunction = feature.getStyleFunction();
+ if (featureStyleFunction) {
+ return featureStyleFunction.call(feature, resolution);
+ } else {
+ return defaultStyle[feature.getGeometry().getType()];
+ }
+};
+
var map = new ol.Map({
interactions: ol.interaction.defaults().extend([
new ol.interaction.DragAndDrop({
formatConstructors: [
- ol.format.KML
- ]
+ ol.format.GPX,
+ ol.format.GeoJSON,
+ ol.format.IGC,
+ ol.format.KML,
+ ol.format.TopoJSON
+ ],
+ styleFunction: styleFunction
})
]),
layers: [
diff --git a/examples/gpx.html b/examples/gpx.html
new file mode 100644
index 0000000000..6e12988b2b
--- /dev/null
+++ b/examples/gpx.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
GPX example
+
+
+
+
+
+
+
+
+
+
+
+
+
GPX example
+
Example of using the GPX source.
+
+
GPX
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/gpx.js b/examples/gpx.js
new file mode 100644
index 0000000000..181f5b56a7
--- /dev/null
+++ b/examples/gpx.js
@@ -0,0 +1,94 @@
+goog.require('ol.Map');
+goog.require('ol.RendererHint');
+goog.require('ol.View2D');
+goog.require('ol.layer.Tile');
+goog.require('ol.layer.Vector');
+goog.require('ol.source.BingMaps');
+goog.require('ol.source.GPX');
+goog.require('ol.style.Circle');
+goog.require('ol.style.Fill');
+goog.require('ol.style.Stroke');
+goog.require('ol.style.Style');
+
+var raster = new ol.layer.Tile({
+ source: new ol.source.BingMaps({
+ imagerySet: 'Aerial',
+ key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3'
+ })
+});
+
+var style = {
+ 'Point': [new ol.style.Style({
+ image: new ol.style.Circle({
+ fill: new ol.style.Fill({
+ color: 'rgba(255,255,0,0.4)'
+ }),
+ radius: 5,
+ stroke: new ol.style.Stroke({
+ color: '#ff0',
+ width: 1
+ })
+ })
+ })],
+ 'LineString': [new ol.style.Style({
+ stroke: new ol.style.Stroke({
+ color: '#f00',
+ width: 3
+ })
+ })],
+ 'MultiLineString': [new ol.style.Style({
+ stroke: new ol.style.Stroke({
+ color: '#0f0',
+ width: 3
+ })
+ })]
+};
+
+var vector = new ol.layer.Vector({
+ source: new ol.source.GPX({
+ reprojectTo: 'EPSG:3857',
+ url: 'data/gpx/fells_loop.gpx'
+ }),
+ styleFunction: function(feature, resolution) {
+ return style[feature.getGeometry().getType()];
+ }
+});
+
+var map = new ol.Map({
+ layers: [raster, vector],
+ renderer: ol.RendererHint.CANVAS,
+ target: document.getElementById('map'),
+ view: new ol.View2D({
+ center: [-7916041.528716288, 5228379.045749711],
+ zoom: 12
+ })
+});
+
+var displayFeatureInfo = function(pixel) {
+ var features = [];
+ map.forEachFeatureAtPixel(pixel, function(feature, layer) {
+ features.push(feature);
+ });
+ if (features.length > 0) {
+ var info = [];
+ var i, ii;
+ for (i = 0, ii = features.length; i < ii; ++i) {
+ info.push(features[i].get('desc'));
+ }
+ document.getElementById('info').innerHTML = info.join(', ') || '(unknown)';
+ map.getTarget().style.cursor = 'pointer';
+ } else {
+ document.getElementById('info').innerHTML = ' ';
+ map.getTarget().style.cursor = '';
+ }
+};
+
+$(map.getViewport()).on('mousemove', function(evt) {
+ var pixel = map.getEventPixel(evt.originalEvent);
+ displayFeatureInfo(pixel);
+});
+
+map.on('singleclick', function(evt) {
+ var pixel = evt.getPixel();
+ displayFeatureInfo(pixel);
+});
diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc
index 20bc1e9ac5..9cbdc7efbd 100644
--- a/src/objectliterals.jsdoc
+++ b/src/objectliterals.jsdoc
@@ -291,6 +291,8 @@
* features will be added to this layer's source. If neither `source` nor
* `layer` are defined then the dropped features will be added as a new
* layer.
+ * @property {ol.feature.StyleFunction|undefined} styleFunction Style function.
+ * This is used to configure any new layers created.
*/
/**
@@ -554,6 +556,20 @@
* @property {Array.
|undefined} urls URLs.
*/
+/**
+ * @typedef {Object} olx.source.GPXOptions
+ * @property {Array.|undefined} attributions Attributions.
+ * @property {Document|undefined} doc Document.
+ * @property {ol.Extent|undefined} extent Extent.
+ * @property {string|undefined} logo Logo.
+ * @property {Node|undefined| node Node.
+ * @property {ol.proj.ProjectionLike} projection Projection.
+ * @property {ol.proj.ProjectionLike} reprojectTo Re-project to.
+ * @property {string|undefined} text Text.
+ * @property {string|undefined} url URL.
+ * @property {Array.|undefined} urls URLs.
+ */
+
/**
* @typedef {Object} olx.source.TopoJSONOptions
* @property {Array.|undefined} attributions Attributions.
diff --git a/src/ol/format/gpxformat.exports b/src/ol/format/gpxformat.exports
new file mode 100644
index 0000000000..82582fd4ac
--- /dev/null
+++ b/src/ol/format/gpxformat.exports
@@ -0,0 +1,3 @@
+@exportSymbol ol.format.GPX
+@exportProperty ol.format.GPX.prototype.readFeature
+@exportProperty ol.format.GPX.prototype.readFeatures
diff --git a/src/ol/format/gpxformat.js b/src/ol/format/gpxformat.js
new file mode 100644
index 0000000000..5b1b1e0ddb
--- /dev/null
+++ b/src/ol/format/gpxformat.js
@@ -0,0 +1,420 @@
+goog.provide('ol.format.GPX');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.dom.NodeType');
+goog.require('goog.object');
+goog.require('ol.Feature');
+goog.require('ol.format.XML');
+goog.require('ol.format.XSD');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.Point');
+goog.require('ol.proj');
+goog.require('ol.xml');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.format.XML}
+ */
+ol.format.GPX = function() {
+ goog.base(this);
+};
+goog.inherits(ol.format.GPX, ol.format.XML);
+
+
+/**
+ * @const
+ * @private
+ * @type {Array.}
+ */
+ol.format.GPX.NAMESPACE_URIS_ = [
+ null,
+ 'http://www.topografix.com/GPX/1/0',
+ 'http://www.topografix.com/GPX/1/1'
+];
+
+
+/**
+ * @param {Array.} flatCoordinates Flat coordinates.
+ * @param {Node} node Node.
+ * @param {Object} values Values.
+ * @private
+ * @return {Array.} Flat coordinates.
+ */
+ol.format.GPX.appendCoordinate_ = function(flatCoordinates, node, values) {
+ goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
+ flatCoordinates.push(
+ parseFloat(node.getAttribute('lon')),
+ parseFloat(node.getAttribute('lat')));
+ if (goog.object.containsKey(values, 'ele')) {
+ flatCoordinates.push(
+ /** @type {number} */ (goog.object.get(values, 'ele')));
+ goog.object.remove(values, 'ele');
+ } else {
+ flatCoordinates.push(0);
+ }
+ if (goog.object.containsKey(values, 'time')) {
+ flatCoordinates.push(
+ /** @type {number} */ (goog.object.get(values, 'time')));
+ goog.object.remove(values, 'time');
+ } else {
+ flatCoordinates.push(0);
+ }
+ return flatCoordinates;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.parseLink_ = function(node, objectStack) {
+ goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
+ goog.asserts.assert(node.localName == 'link');
+ var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var href = node.getAttribute('href');
+ if (!goog.isNull(href)) {
+ goog.object.set(values, 'link', href);
+ }
+ ol.xml.parse(ol.format.GPX.LINK_PARSERS_, node, objectStack);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.parseRtePt_ = function(node, objectStack) {
+ goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
+ goog.asserts.assert(node.localName == 'rtept');
+ var values = ol.xml.pushAndParse(
+ {}, ol.format.GPX.RTEPT_PARSERS_, node, objectStack);
+ if (goog.isDef(values)) {
+ var rteValues = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var flatCoordinates = /** @type {Array.} */
+ (goog.object.get(rteValues, 'flatCoordinates'));
+ ol.format.GPX.appendCoordinate_(flatCoordinates, node, values);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.parseTrkPt_ = function(node, objectStack) {
+ goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
+ goog.asserts.assert(node.localName == 'trkpt');
+ var values = ol.xml.pushAndParse(
+ {}, ol.format.GPX.TRKPT_PARSERS_, node, objectStack);
+ if (goog.isDef(values)) {
+ var trkValues = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ var flatCoordinates = /** @type {Array.} */
+ (goog.object.get(trkValues, 'flatCoordinates'));
+ ol.format.GPX.appendCoordinate_(flatCoordinates, node, values);
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ */
+ol.format.GPX.parseTrkSeg_ = function(node, objectStack) {
+ goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
+ goog.asserts.assert(node.localName == 'trkseg');
+ var values = /** @type {Object} */ (objectStack[objectStack.length - 1]);
+ ol.xml.parse(ol.format.GPX.TRKSEG_PARSERS_, node, objectStack);
+ var flatCoordinates = /** @type {Array.} */
+ (goog.object.get(values, 'flatCoordinates'));
+ var ends = /** @type {Array.} */ (goog.object.get(values, 'ends'));
+ ends.push(flatCoordinates.length);
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.Feature|undefined} Track.
+ */
+ol.format.GPX.readRte_ = function(node, objectStack) {
+ goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
+ goog.asserts.assert(node.localName == 'rte');
+ var values = ol.xml.pushAndParse({
+ 'flatCoordinates': []
+ }, ol.format.GPX.RTE_PARSERS_, node, objectStack);
+ if (!goog.isDef(values)) {
+ return undefined;
+ }
+ var flatCoordinates = /** @type {Array.} */
+ (goog.object.get(values, 'flatCoordinates'));
+ goog.object.remove(values, 'flatCoordinates');
+ var geometry = new ol.geom.LineString(null);
+ geometry.setFlatCoordinates(ol.geom.GeometryLayout.XYZM, flatCoordinates);
+ var feature = new ol.Feature(geometry);
+ feature.setValues(values);
+ return feature;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.Feature|undefined} Track.
+ */
+ol.format.GPX.readTrk_ = function(node, objectStack) {
+ goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
+ goog.asserts.assert(node.localName == 'trk');
+ var values = ol.xml.pushAndParse({
+ 'flatCoordinates': [],
+ 'ends': []
+ }, ol.format.GPX.TRK_PARSERS_, node, objectStack);
+ if (!goog.isDef(values)) {
+ return undefined;
+ }
+ var flatCoordinates = /** @type {Array.} */
+ (goog.object.get(values, 'flatCoordinates'));
+ goog.object.remove(values, 'flatCoordinates');
+ var ends = /** @type {Array.} */ (goog.object.get(values, 'ends'));
+ goog.object.remove(values, 'ends');
+ var geometry = new ol.geom.MultiLineString(null);
+ geometry.setFlatCoordinates(
+ ol.geom.GeometryLayout.XYZM, flatCoordinates, ends);
+ var feature = new ol.Feature(geometry);
+ feature.setValues(values);
+ return feature;
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @param {Array.<*>} objectStack Object stack.
+ * @private
+ * @return {ol.Feature|undefined} Waypoint.
+ */
+ol.format.GPX.readWpt_ = function(node, objectStack) {
+ goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
+ goog.asserts.assert(node.localName == 'wpt');
+ var values = ol.xml.pushAndParse(
+ {}, ol.format.GPX.WPT_PARSERS_, node, objectStack);
+ if (!goog.isDef(values)) {
+ return undefined;
+ }
+ var coordinates = ol.format.GPX.appendCoordinate_([], node, values);
+ var geometry = new ol.geom.Point(
+ coordinates, ol.geom.GeometryLayout.XYZM);
+ var feature = new ol.Feature(geometry);
+ feature.setValues(values);
+ return feature;
+};
+
+
+/**
+ * @const
+ * @type {Object.): (ol.Feature|undefined)>}
+ * @private
+ */
+ol.format.GPX.FEATURE_READER_ = {
+ 'rte': ol.format.GPX.readRte_,
+ 'trk': ol.format.GPX.readTrk_,
+ 'wpt': ol.format.GPX.readWpt_
+};
+
+
+/**
+ * @const
+ * @type {Object.>}
+ * @private
+ */
+ol.format.GPX.GPX_PARSERS_ = ol.xml.makeParsersNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'rte': ol.xml.makeArrayPusher(ol.format.GPX.readRte_),
+ 'trk': ol.xml.makeArrayPusher(ol.format.GPX.readTrk_),
+ 'wpt': ol.xml.makeArrayPusher(ol.format.GPX.readWpt_)
+ });
+
+
+/**
+ * @const
+ * @type {Object.>}
+ * @private
+ */
+ol.format.GPX.LINK_PARSERS_ = ol.xml.makeParsersNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'text':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readString, 'linkText'),
+ 'type':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readString, 'linkType')
+ });
+
+
+/**
+ * @const
+ * @type {Object.>}
+ * @private
+ */
+ol.format.GPX.RTE_PARSERS_ = ol.xml.makeParsersNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'link': ol.format.GPX.parseLink_,
+ 'number':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger),
+ 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'rtept': ol.format.GPX.parseRtePt_
+ });
+
+
+/**
+ * @const
+ * @type {Object.>}
+ * @private
+ */
+ol.format.GPX.RTEPT_PARSERS_ = ol.xml.makeParsersNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime)
+ });
+
+
+/**
+ * @const
+ * @type {Object.>}
+ * @private
+ */
+ol.format.GPX.TRK_PARSERS_ = ol.xml.makeParsersNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'link': ol.format.GPX.parseLink_,
+ 'number':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger),
+ 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'trkseg': ol.format.GPX.parseTrkSeg_
+ });
+
+
+/**
+ * @const
+ * @type {Object.>}
+ * @private
+ */
+ol.format.GPX.TRKSEG_PARSERS_ = ol.xml.makeParsersNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'trkpt': ol.format.GPX.parseTrkPt_
+ });
+
+
+/**
+ * @const
+ * @type {Object.>}
+ * @private
+ */
+ol.format.GPX.TRKPT_PARSERS_ = ol.xml.makeParsersNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime)
+ });
+
+
+/**
+ * @const
+ * @type {Object.>}
+ * @private
+ */
+ol.format.GPX.WPT_PARSERS_ = ol.xml.makeParsersNS(
+ ol.format.GPX.NAMESPACE_URIS_, {
+ 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime),
+ 'magvar': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'geoidheight': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'link': ol.format.GPX.parseLink_,
+ 'sym': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'fix': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString),
+ 'sat': ol.xml.makeObjectPropertySetter(
+ ol.format.XSD.readNonNegativeInteger),
+ 'hdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'vdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'pdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'ageofdgpsdata':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal),
+ 'dgpsid':
+ ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger)
+ });
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GPX.prototype.readFeatureFromNode = function(node) {
+ goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
+ if (goog.array.indexOf(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI) ==
+ -1) {
+ return null;
+ }
+ var featureReader = ol.format.GPX.FEATURE_READER_[node.localName];
+ if (!goog.isDef(featureReader)) {
+ return null;
+ }
+ var feature = featureReader(node, []);
+ if (!goog.isDef(feature)) {
+ return null;
+ }
+ return feature;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GPX.prototype.readFeaturesFromNode = function(node) {
+ goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT);
+ if (goog.array.indexOf(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI) ==
+ -1) {
+ return [];
+ }
+ if (node.localName == 'gpx') {
+ var features = ol.xml.pushAndParse(/** @type {Array.} */ ([]),
+ ol.format.GPX.GPX_PARSERS_, node, []);
+ if (goog.isDef(features)) {
+ return features;
+ } else {
+ return [];
+ }
+ }
+ return [];
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GPX.prototype.readProjectionFromDocument = function(doc) {
+ return ol.proj.get('EPSG:4326');
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.format.GPX.prototype.readProjectionFromNode = function(node) {
+ return ol.proj.get('EPSG:4326');
+};
diff --git a/src/ol/format/xsdformat.js b/src/ol/format/xsdformat.js
new file mode 100644
index 0000000000..71a2a9f9dd
--- /dev/null
+++ b/src/ol/format/xsdformat.js
@@ -0,0 +1,83 @@
+goog.provide('ol.format.XSD');
+
+goog.require('goog.string');
+goog.require('ol.xml');
+
+
+/**
+ * @const
+ * @type {string}
+ */
+ol.format.XSD.NAMESPACE_URI = 'http://www.w3.org/2001/XMLSchema';
+
+
+/**
+ * @param {Node} node Node.
+ * @return {number|undefined} DateTime.
+ */
+ol.format.XSD.readDateTime = function(node) {
+ var s = ol.xml.getAllTextContent(node, false);
+ var re =
+ /^\s*(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|(?:([+\-])(\d{2})(?::(\d{2}))?))\s*$/;
+ var m = re.exec(s);
+ if (m) {
+ var year = parseInt(m[1], 10);
+ var month = parseInt(m[2], 10) - 1;
+ var day = parseInt(m[3], 10);
+ var hour = parseInt(m[4], 10);
+ var minute = parseInt(m[5], 10);
+ var second = parseInt(m[6], 10);
+ var dateTime = Date.UTC(year, month, day, hour, minute, second, 0) / 1000;
+ if (m[7] != 'Z') {
+ var sign = m[8] == '-' ? -1 : 1;
+ dateTime += sign * 60 * parseInt(m[9], 10);
+ if (goog.isDef(m[10])) {
+ dateTime += sign * 60 * 60 * parseInt(m[10], 10);
+ }
+ }
+ return dateTime;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {number|undefined} Decimal.
+ */
+ol.format.XSD.readDecimal = function(node) {
+ // FIXME check spec
+ var s = ol.xml.getAllTextContent(node, false);
+ var m = /^\s*([+\-]?\d+(?:\.\d*)?)\s*$/.exec(s);
+ if (m) {
+ return parseFloat(m[1]);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {number|undefined} Decimal.
+ */
+ol.format.XSD.readNonNegativeInteger = function(node) {
+ var s = ol.xml.getAllTextContent(node, false);
+ var m = /^\s*(\d+)\s*$/.exec(s);
+ if (m) {
+ return parseInt(m[1], 10);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {Node} node Node.
+ * @return {string|undefined} String.
+ */
+ol.format.XSD.readString = function(node) {
+ var s = ol.xml.getAllTextContent(node, false);
+ return goog.string.trim(s);
+};
diff --git a/src/ol/interaction/draganddropinteraction.js b/src/ol/interaction/draganddropinteraction.js
index 0ed09d211f..329a3c5172 100644
--- a/src/ol/interaction/draganddropinteraction.js
+++ b/src/ol/interaction/draganddropinteraction.js
@@ -52,6 +52,12 @@ ol.interaction.DragAndDrop = function(opt_options) {
*/
this.layer_ = goog.isDef(options.layer) ? options.layer : null;
+ /**
+ * @private
+ * @type {ol.feature.StyleFunction|undefined}
+ */
+ this.styleFunction_ = options.styleFunction;
+
/**
* @private
* @type {goog.events.FileDropHandler}
@@ -149,6 +155,7 @@ ol.interaction.DragAndDrop.prototype.handleResult_ = function(result) {
}
if (goog.isNull(this.layer_)) {
map.getLayers().push(new ol.layer.Vector({
+ styleFunction: this.styleFunction_,
source: source
}));
}
diff --git a/src/ol/source/gpxsource.exports b/src/ol/source/gpxsource.exports
new file mode 100644
index 0000000000..0dfabb47be
--- /dev/null
+++ b/src/ol/source/gpxsource.exports
@@ -0,0 +1 @@
+@exportSymbol ol.source.GPX
diff --git a/src/ol/source/gpxsource.js b/src/ol/source/gpxsource.js
new file mode 100644
index 0000000000..4cf8da99ce
--- /dev/null
+++ b/src/ol/source/gpxsource.js
@@ -0,0 +1,32 @@
+goog.provide('ol.source.GPX');
+
+goog.require('ol.format.GPX');
+goog.require('ol.source.VectorFile');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.source.VectorFile}
+ * @param {olx.source.GPXOptions=} opt_options Options.
+ */
+ol.source.GPX = function(opt_options) {
+
+ var options = goog.isDef(opt_options) ? opt_options : {};
+
+ goog.base(this, {
+ attributions: options.attributions,
+ doc: options.doc,
+ extent: options.extent,
+ format: new ol.format.GPX(),
+ logo: options.logo,
+ node: options.node,
+ projection: options.projection,
+ reprojectTo: options.reprojectTo,
+ text: options.text,
+ url: options.url,
+ urls: options.urls
+ });
+
+};
+goog.inherits(ol.source.GPX, ol.source.VectorFile);
diff --git a/test/spec/ol/format/gpxformat.test.js b/test/spec/ol/format/gpxformat.test.js
new file mode 100644
index 0000000000..b146289a61
--- /dev/null
+++ b/test/spec/ol/format/gpxformat.test.js
@@ -0,0 +1,380 @@
+goog.provide('ol.test.format.GPX');
+
+
+describe('ol.format.GPX', function() {
+
+ var format;
+ beforeEach(function() {
+ format = new ol.format.GPX();
+ });
+
+ describe('readFeatures', function() {
+
+ describe('rte', function() {
+
+ it('can read an empty rte', function() {
+ var text =
+ '' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ var g = f.getGeometry();
+ expect(g).to.be.an(ol.geom.LineString);
+ expect(g.getCoordinates()).to.eql([]);
+ expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
+ });
+
+ it('can read various rte attributes', function() {
+ var text =
+ '' +
+ ' ' +
+ ' Name' +
+ ' Comment' +
+ ' Description' +
+ ' Source' +
+ ' ' +
+ ' Link text' +
+ ' Link type' +
+ ' ' +
+ ' 1' +
+ ' Type' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ expect(f.get('name')).to.be('Name');
+ expect(f.get('cmt')).to.be('Comment');
+ expect(f.get('desc')).to.be('Description');
+ expect(f.get('src')).to.be('Source');
+ expect(f.get('link')).to.be('http://example.com/');
+ expect(f.get('linkText')).to.be('Link text');
+ expect(f.get('linkType')).to.be('Link type');
+ expect(f.get('number')).to.be(1);
+ expect(f.get('type')).to.be('Type');
+ });
+
+ it('can read a rte with multiple rtepts', function() {
+ var text =
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ var g = f.getGeometry();
+ 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);
+ });
+
+ });
+
+ describe('trk', function() {
+
+ it('can read an empty trk', function() {
+ var text =
+ '' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ var g = f.getGeometry();
+ expect(g).to.be.an(ol.geom.MultiLineString);
+ expect(g.getCoordinates()).to.eql([]);
+ expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
+ });
+
+ it('can read various trk attributes', function() {
+ var text =
+ '' +
+ ' ' +
+ ' Name' +
+ ' Comment' +
+ ' Description' +
+ ' Source' +
+ ' ' +
+ ' Link text' +
+ ' Link type' +
+ ' ' +
+ ' 1' +
+ ' Type' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ expect(f.get('name')).to.be('Name');
+ expect(f.get('cmt')).to.be('Comment');
+ expect(f.get('desc')).to.be('Description');
+ expect(f.get('src')).to.be('Source');
+ expect(f.get('link')).to.be('http://example.com/');
+ expect(f.get('linkText')).to.be('Link text');
+ expect(f.get('linkType')).to.be('Link type');
+ expect(f.get('number')).to.be(1);
+ expect(f.get('type')).to.be('Type');
+ });
+
+ it('can read a trk with an empty trkseg', function() {
+ var text =
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ var g = f.getGeometry();
+ expect(g).to.be.an(ol.geom.MultiLineString);
+ expect(g.getCoordinates()).to.eql([[]]);
+ expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
+ });
+
+ it('can read a trk with a trkseg with multiple trkpts', function() {
+ var text =
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' 3' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' 7' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ var g = f.getGeometry();
+ expect(g).to.be.an(ol.geom.MultiLineString);
+ expect(g.getCoordinates()).to.eql([
+ [[2, 1, 3, 1263115752], [6, 5, 7, 1263115812]]
+ ]);
+ expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
+ });
+
+ it('can read a trk with multiple trksegs', function() {
+ var text =
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' 3' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' 7' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' 10' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' 13' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ var g = f.getGeometry();
+ expect(g).to.be.an(ol.geom.MultiLineString);
+ expect(g.getCoordinates()).to.eql([
+ [[2, 1, 3, 1263115752], [6, 5, 7, 1263115812]],
+ [[9, 8, 10, 1263115872], [12, 11, 13, 1263115932]]
+ ]);
+ expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM);
+ });
+
+ });
+
+ describe('wpt', function() {
+
+ it('can read a wpt', function() {
+ var text =
+ '' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ var g = f.getGeometry();
+ 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);
+ });
+
+ it('can read a wpt with ele', function() {
+ var text =
+ '' +
+ ' ' +
+ ' 3' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ var g = f.getGeometry();
+ 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);
+ });
+
+ it('can read a wpt with time', function() {
+ var text =
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ var g = f.getGeometry();
+ 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);
+ });
+
+ it('can read a wpt with ele and time', function() {
+ var text =
+ '' +
+ ' ' +
+ ' 3' +
+ ' ' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ var g = f.getGeometry();
+ 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);
+ });
+
+ it('can read various wpt attributes', function() {
+ var text =
+ '' +
+ ' ' +
+ ' 11' +
+ ' 4' +
+ ' Name' +
+ ' Comment' +
+ ' Description' +
+ ' Source' +
+ ' ' +
+ ' Link text' +
+ ' Link type' +
+ ' ' +
+ ' Symbol' +
+ ' Type' +
+ ' 2d' +
+ ' 5' +
+ ' 6' +
+ ' 7' +
+ ' 8' +
+ ' 9' +
+ ' 10' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(1);
+ var f = fs[0];
+ expect(f).to.be.an(ol.Feature);
+ expect(f.get('magvar')).to.be(11);
+ expect(f.get('geoidheight')).to.be(4);
+ expect(f.get('name')).to.be('Name');
+ expect(f.get('cmt')).to.be('Comment');
+ expect(f.get('desc')).to.be('Description');
+ expect(f.get('src')).to.be('Source');
+ expect(f.get('link')).to.be('http://example.com/');
+ expect(f.get('linkText')).to.be('Link text');
+ expect(f.get('linkType')).to.be('Link type');
+ expect(f.get('sym')).to.be('Symbol');
+ expect(f.get('type')).to.be('Type');
+ expect(f.get('fix')).to.be('2d');
+ expect(f.get('hdop')).to.be(6);
+ expect(f.get('vdop')).to.be(7);
+ expect(f.get('pdop')).to.be(8);
+ expect(f.get('ageofdgpsdata')).to.be(9);
+ expect(f.get('dgpsid')).to.be(10);
+ });
+
+ });
+
+ describe('XML namespace support', function() {
+
+ it('can read features with a version 1.0 namespace', function() {
+ var text =
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(3);
+ });
+
+ it('can read features with a version 1.1 namespace', function() {
+ var text =
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(3);
+ });
+
+ it('can read features with no namespace', function() {
+ var text =
+ '' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '';
+ var fs = format.readFeatures(text);
+ expect(fs).to.have.length(3);
+ });
+
+ });
+
+ });
+
+});
+
+
+goog.require('ol.Feature');
+goog.require('ol.format.GPX');
+goog.require('ol.geom.LineString');
+goog.require('ol.geom.MultiLineString');
+goog.require('ol.geom.Point');