diff --git a/src/ol/format/gpx.js b/src/ol/format/gpx.js index 3dbe647b78..91af5bbdd8 100644 --- a/src/ol/format/gpx.js +++ b/src/ol/format/gpx.js @@ -66,12 +66,13 @@ ol.format.GPX.SCHEMA_LOCATION_ = 'http://www.topografix.com/GPX/1/1 ' + /** * @param {Array.} flatCoordinates Flat coordinates. + * @param {ol.LayoutOptions} layoutOptions Layout options. * @param {Node} node Node. * @param {Object} values Values. * @private * @return {Array.} Flat coordinates. */ -ol.format.GPX.appendCoordinate_ = function(flatCoordinates, node, values) { +ol.format.GPX.appendCoordinate_ = function(flatCoordinates, layoutOptions, node, values) { ol.DEBUG && console.assert(node.nodeType == Node.ELEMENT_NODE, 'node.nodeType should be ELEMENT'); flatCoordinates.push( @@ -80,12 +81,14 @@ ol.format.GPX.appendCoordinate_ = function(flatCoordinates, node, values) { if ('ele' in values) { flatCoordinates.push(/** @type {number} */ (values['ele'])); delete values['ele']; + layoutOptions.hasZ = true; } else { flatCoordinates.push(0); } if ('time' in values) { flatCoordinates.push(/** @type {number} */ (values['time'])); delete values['time']; + layoutOptions.hasM = true; } else { flatCoordinates.push(0); } @@ -93,6 +96,51 @@ ol.format.GPX.appendCoordinate_ = function(flatCoordinates, node, values) { }; +/** + * Choose GeometryLayout based on flags in layoutOptions and adjust flatCoordinates + * and ends arrays by shrinking them accordingly (removing unused zero entries). + * + * @param {ol.LayoutOptions} layoutOptions Layout options. + * @param {Array.} flatCoordinates Flat coordinates. + * @param {Array.=} ends Ends. + * @return {ol.geom.GeometryLayout} Layout. + */ +ol.format.GPX.applyLayoutOptions_ = function(layoutOptions, flatCoordinates, ends) { + var layout = ol.geom.GeometryLayout.XY; + var stride = 2; + if (layoutOptions.hasZ && layoutOptions.hasM) { + layout = ol.geom.GeometryLayout.XYZM; + stride = 4; + } else if (layoutOptions.hasZ) { + layout = ol.geom.GeometryLayout.XYZ; + stride = 3; + } else if (layoutOptions.hasM) { + layout = ol.geom.GeometryLayout.XYM; + stride = 3; + } + if (stride !== 4) { + var i, ii; + for (i = 0, ii = flatCoordinates.length / 4; i < ii; i++) { + flatCoordinates[i * stride] = flatCoordinates[i * 4]; + flatCoordinates[i * stride + 1] = flatCoordinates[i * 4 + 1]; + if (layoutOptions.hasZ) { + flatCoordinates[i * stride + 2] = flatCoordinates[i * 4 + 2]; + } + if (layoutOptions.hasM) { + flatCoordinates[i * stride + 2] = flatCoordinates[i * 4 + 3]; + } + } + flatCoordinates.length = flatCoordinates.length / 4 * stride; + if (ends) { + for (i = 0, ii = ends.length; i < ii; i++) { + ends[i] = ends[i] / 4 * stride; + } + } + } + return layout; +}; + + /** * @param {Node} node Node. * @param {Array.<*>} objectStack Object stack. @@ -141,7 +189,9 @@ ol.format.GPX.parseRtePt_ = function(node, objectStack) { var rteValues = /** @type {Object} */ (objectStack[objectStack.length - 1]); var flatCoordinates = /** @type {Array.} */ (rteValues['flatCoordinates']); - ol.format.GPX.appendCoordinate_(flatCoordinates, node, values); + var layoutOptions = /** @type {ol.LayoutOptions} */ + (rteValues['layoutOptions']); + ol.format.GPX.appendCoordinate_(flatCoordinates, layoutOptions, node, values); } }; @@ -161,7 +211,9 @@ ol.format.GPX.parseTrkPt_ = function(node, objectStack) { var trkValues = /** @type {Object} */ (objectStack[objectStack.length - 1]); var flatCoordinates = /** @type {Array.} */ (trkValues['flatCoordinates']); - ol.format.GPX.appendCoordinate_(flatCoordinates, node, values); + var layoutOptions = /** @type {ol.LayoutOptions} */ + (trkValues['layoutOptions']); + ol.format.GPX.appendCoordinate_(flatCoordinates, layoutOptions, node, values); } }; @@ -197,7 +249,8 @@ ol.format.GPX.readRte_ = function(node, objectStack) { ol.DEBUG && console.assert(node.localName == 'rte', 'localName should be rte'); var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); var values = ol.xml.pushParseAndPop({ - 'flatCoordinates': [] + 'flatCoordinates': [], + 'layoutOptions': {} }, ol.format.GPX.RTE_PARSERS_, node, objectStack); if (!values) { return undefined; @@ -205,8 +258,11 @@ ol.format.GPX.readRte_ = function(node, objectStack) { var flatCoordinates = /** @type {Array.} */ (values['flatCoordinates']); delete values['flatCoordinates']; + var layoutOptions = /** @type {ol.LayoutOptions} */ (values['layoutOptions']); + delete values['layoutOptions']; + var layout = ol.format.GPX.applyLayoutOptions_(layoutOptions, flatCoordinates); var geometry = new ol.geom.LineString(null); - geometry.setFlatCoordinates(ol.geom.GeometryLayout.XYZM, flatCoordinates); + geometry.setFlatCoordinates(layout, flatCoordinates); ol.format.Feature.transformWithOptions(geometry, false, options); var feature = new ol.Feature(geometry); feature.setProperties(values); @@ -227,7 +283,8 @@ ol.format.GPX.readTrk_ = function(node, objectStack) { var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); var values = ol.xml.pushParseAndPop({ 'flatCoordinates': [], - 'ends': [] + 'ends': [], + 'layoutOptions': {} }, ol.format.GPX.TRK_PARSERS_, node, objectStack); if (!values) { return undefined; @@ -237,9 +294,11 @@ ol.format.GPX.readTrk_ = function(node, objectStack) { delete values['flatCoordinates']; var ends = /** @type {Array.} */ (values['ends']); delete values['ends']; + var layoutOptions = /** @type {ol.LayoutOptions} */ (values['layoutOptions']); + delete values['layoutOptions']; + var layout = ol.format.GPX.applyLayoutOptions_(layoutOptions, flatCoordinates, ends); var geometry = new ol.geom.MultiLineString(null); - geometry.setFlatCoordinates( - ol.geom.GeometryLayout.XYZM, flatCoordinates, ends); + geometry.setFlatCoordinates(layout, flatCoordinates, ends); ol.format.Feature.transformWithOptions(geometry, false, options); var feature = new ol.Feature(geometry); feature.setProperties(values); @@ -263,9 +322,10 @@ ol.format.GPX.readWpt_ = function(node, objectStack) { if (!values) { return undefined; } - var coordinates = ol.format.GPX.appendCoordinate_([], node, values); - var geometry = new ol.geom.Point( - coordinates, ol.geom.GeometryLayout.XYZM); + var layoutOptions = /** @type {ol.LayoutOptions} */ ({}); + var coordinates = ol.format.GPX.appendCoordinate_([], layoutOptions, node, values); + var layout = ol.format.GPX.applyLayoutOptions_(layoutOptions, coordinates); + var geometry = new ol.geom.Point(coordinates, layout); ol.format.Feature.transformWithOptions(geometry, false, options); var feature = new ol.Feature(geometry); feature.setProperties(values); diff --git a/src/ol/typedefs.js b/src/ol/typedefs.js index fdbbd0fd48..90a9684a66 100644 --- a/src/ol/typedefs.js +++ b/src/ol/typedefs.js @@ -311,6 +311,12 @@ ol.KMLGxTrackObject_; ol.LayerState; +/** + * @typedef {{hasZ: (boolean|undefined), hasM: (boolean|undefined)}} + */ +ol.LayoutOptions; + + /** * A function that takes an {@link ol.Extent} and a resolution as arguments, and * returns an array of {@link ol.Extent} with the extents to load. Usually this diff --git a/test/spec/ol/format/gpx.test.js b/test/spec/ol/format/gpx.test.js index a6f9dc7e63..e06ec98ae9 100644 --- a/test/spec/ol/format/gpx.test.js +++ b/test/spec/ol/format/gpx.test.js @@ -42,7 +42,7 @@ describe('ol.format.GPX', function() { var g = f.getGeometry(); expect(g).to.be.an(ol.geom.LineString); expect(g.getCoordinates()).to.eql([]); - expect(g.getLayout()).to.be('XYZM'); + expect(g.getLayout()).to.be('XY'); }); it('can read and write various rte attributes', function() { @@ -98,8 +98,8 @@ describe('ol.format.GPX', function() { 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('XYZM'); + expect(g.getCoordinates()).to.eql([[2, 1], [4, 3]]); + expect(g.getLayout()).to.be('XY'); var serialized = format.writeFeaturesNode(fs); expect(serialized).to.xmleql(ol.xml.parse(text)); }); @@ -124,11 +124,9 @@ describe('ol.format.GPX', function() { var g = f.getGeometry(); expect(g).to.be.an(ol.geom.LineString); var p1 = ol.proj.transform([2, 1], 'EPSG:4326', 'EPSG:3857'); - p1.push(0, 0); var p2 = ol.proj.transform([6, 5], 'EPSG:4326', 'EPSG:3857'); - p2.push(0, 0); expect(g.getCoordinates()).to.eql([p1, p2]); - expect(g.getLayout()).to.be('XYZM'); + expect(g.getLayout()).to.be('XY'); var serialized = format.writeFeaturesNode(fs, { featureProjection: 'EPSG:3857' }); @@ -168,7 +166,7 @@ describe('ol.format.GPX', function() { var g = f.getGeometry(); expect(g).to.be.an(ol.geom.MultiLineString); expect(g.getCoordinates()).to.eql([]); - expect(g.getLayout()).to.be('XYZM'); + expect(g.getLayout()).to.be('XY'); }); it('can read and write various trk attributes', function() { @@ -224,7 +222,7 @@ describe('ol.format.GPX', function() { var g = f.getGeometry(); expect(g).to.be.an(ol.geom.MultiLineString); expect(g.getCoordinates()).to.eql([[]]); - expect(g.getLayout()).to.be('XYZM'); + expect(g.getLayout()).to.be('XY'); var serialized = format.writeFeaturesNode(fs); expect(serialized).to.xmleql(ol.xml.parse(text)); }); @@ -398,8 +396,8 @@ describe('ol.format.GPX', function() { 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('XYZM'); + expect(g.getCoordinates()).to.eql([2, 1]); + expect(g.getLayout()).to.be('XY'); var serialized = format.writeFeaturesNode(fs); expect(serialized).to.xmleql(ol.xml.parse(text)); }); @@ -421,9 +419,8 @@ describe('ol.format.GPX', function() { var g = f.getGeometry(); expect(g).to.be.an(ol.geom.Point); var expectedPoint = ol.proj.transform([2, 1], 'EPSG:4326', 'EPSG:3857'); - expectedPoint.push(0, 0); expect(g.getCoordinates()).to.eql(expectedPoint); - expect(g.getLayout()).to.be('XYZM'); + expect(g.getLayout()).to.be('XY'); var serialized = format.writeFeaturesNode(fs, { featureProjection: 'EPSG:3857' }); @@ -446,8 +443,8 @@ describe('ol.format.GPX', function() { 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('XYZM'); + expect(g.getCoordinates()).to.eql([2, 1, 3]); + expect(g.getLayout()).to.be('XYZ'); var serialized = format.writeFeaturesNode(fs); expect(serialized).to.xmleql(ol.xml.parse(text)); }); @@ -468,8 +465,8 @@ describe('ol.format.GPX', function() { 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('XYZM'); + expect(g.getCoordinates()).to.eql([2, 1, 1263115752]); + expect(g.getLayout()).to.be('XYM'); var serialized = format.writeFeaturesNode(fs); expect(serialized).to.xmleql(ol.xml.parse(text)); });