diff --git a/src/ol/format/featureformat.js b/src/ol/format/featureformat.js index aad4e4a91e..d7176bcde0 100644 --- a/src/ol/format/featureformat.js +++ b/src/ol/format/featureformat.js @@ -124,15 +124,15 @@ ol.format.Feature.prototype.writeGeometry = goog.abstractMethod; /** * @param {ol.geom.Geometry} geometry Geometry. - * @param {boolean} write Set to true for writing, false for reading. For - * writing, the geometry will be cloned before transforming. + * @param {boolean} write Set to true for writing, false for reading. + * @param {boolean} clone The geometry will be cloned before transforming. * @param {(olx.format.WriteOptions|olx.format.ReadOptions)=} opt_options * Options. * @return {ol.geom.Geometry} Transformed geometry. * @protected */ ol.format.Feature.transformWithOptions = function( - geometry, write, opt_options) { + geometry, write, clone, opt_options) { var featureProjection = goog.isDef(opt_options) ? ol.proj.get(opt_options.featureProjection) : null; var dataProjection = goog.isDef(opt_options) ? diff --git a/src/ol/format/geojsonformat.js b/src/ol/format/geojsonformat.js index 9b12d76095..f6cfc07a70 100644 --- a/src/ol/format/geojsonformat.js +++ b/src/ol/format/geojsonformat.js @@ -76,7 +76,7 @@ ol.format.GeoJSON.readGeometry_ = function(object, opt_options) { var geometryReader = ol.format.GeoJSON.GEOMETRY_READERS_[object.type]; goog.asserts.assert(goog.isDef(geometryReader)); return ol.format.Feature.transformWithOptions( - geometryReader(object), false, opt_options); + geometryReader(object), false, false, opt_options); }; @@ -177,7 +177,8 @@ ol.format.GeoJSON.writeGeometry_ = function(geometry, opt_options) { var geometryWriter = ol.format.GeoJSON.GEOMETRY_WRITERS_[geometry.getType()]; goog.asserts.assert(goog.isDef(geometryWriter)); return geometryWriter( - ol.format.Feature.transformWithOptions(geometry, true, opt_options)); + ol.format.Feature.transformWithOptions( + geometry, true, true, opt_options)); }; diff --git a/src/ol/format/gmlformat.js b/src/ol/format/gmlformat.js index a4b7cfbaea..ed840a66d8 100644 --- a/src/ol/format/gmlformat.js +++ b/src/ol/format/gmlformat.js @@ -162,7 +162,8 @@ ol.format.GML.readGeometry = function(node, objectStack) { var geometry = ol.xml.pushParseAndPop(/** @type {ol.geom.Geometry} */(null), ol.format.GML.GEOMETRY_PARSERS_, node, objectStack); if (goog.isDefAndNotNull(geometry)) { - return ol.format.Feature.transformWithOptions(geometry, false, context); + return ol.format.Feature.transformWithOptions( + geometry, false, false, context); } else { return undefined; } @@ -1467,7 +1468,8 @@ ol.format.GML.writeGeometry = function(node, geometry, objectStack) { } } else { goog.asserts.assertInstanceof(geometry, ol.geom.Geometry); - value = ol.format.Feature.transformWithOptions(geometry, true, context); + value = + ol.format.Feature.transformWithOptions(geometry, true, true, context); } ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */ (item), ol.format.GML.GEOMETRY_SERIALIZERS_, diff --git a/src/ol/format/gpxformat.js b/src/ol/format/gpxformat.js index af48c40316..d3a7dc9413 100644 --- a/src/ol/format/gpxformat.js +++ b/src/ol/format/gpxformat.js @@ -415,6 +415,7 @@ ol.format.GPX.prototype.handleReadExtensions_ = function(features) { * * @function * @param {ArrayBuffer|Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. * @return {ol.Feature} Feature. * @api */ @@ -424,7 +425,7 @@ ol.format.GPX.prototype.readFeature; /** * @inheritDoc */ -ol.format.GPX.prototype.readFeatureFromNode = function(node) { +ol.format.GPX.prototype.readFeatureFromNode = function(node, opt_options) { goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT); if (!goog.array.contains(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI)) { return null; @@ -438,6 +439,8 @@ ol.format.GPX.prototype.readFeatureFromNode = function(node) { return null; } this.handleReadExtensions_([feature]); + ol.format.XMLFeature.transformFeaturesWithOptions( + [feature], false, this.getReadOptions(node, opt_options)); return feature; }; @@ -447,6 +450,7 @@ ol.format.GPX.prototype.readFeatureFromNode = function(node) { * * @function * @param {ArrayBuffer|Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. * @return {Array.} Features. * @api */ @@ -456,7 +460,7 @@ ol.format.GPX.prototype.readFeatures; /** * @inheritDoc */ -ol.format.GPX.prototype.readFeaturesFromNode = function(node) { +ol.format.GPX.prototype.readFeaturesFromNode = function(node, opt_options) { goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT); if (!goog.array.contains(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI)) { return []; @@ -467,6 +471,8 @@ ol.format.GPX.prototype.readFeaturesFromNode = function(node) { node, []); if (goog.isDef(features)) { this.handleReadExtensions_(features); + ol.format.XMLFeature.transformFeaturesWithOptions( + features, false, this.getReadOptions(node, opt_options)); return features; } else { return []; @@ -845,6 +851,7 @@ ol.format.GPX.GPX_SERIALIZERS_ = ol.xml.makeStructureNS( * * @function * @param {Array.} features Features. + * @param {olx.format.WriteOptions=} opt_options Write options. * @return {Node} Result. * @api */ @@ -854,9 +861,20 @@ ol.format.GPX.prototype.writeFeatures; /** * @inheritDoc */ -ol.format.GPX.prototype.writeFeaturesNode = function(features) { +ol.format.GPX.prototype.writeFeaturesNode = function(features, opt_options) { //FIXME Serialize metadata var gpx = ol.xml.createElementNS('http://www.topografix.com/GPX/1/1', 'gpx'); + if (goog.isDef(opt_options)) { + if (!goog.isDef(opt_options.dataProjection)) { + // for convenience set a default dataProjection + opt_options = { + featureProjection: opt_options.featureProjection, + dataProjection: this.readProjectionFromDocument(null) + }; + } + } + features = ol.format.XMLFeature.transformFeaturesWithOptions( + features, true, opt_options); ol.xml.pushSerializeAndPop(/** @type {ol.xml.NodeStackItem} */ ({node: gpx}), ol.format.GPX.GPX_SERIALIZERS_, ol.format.GPX.GPX_NODE_FACTORY_, features, []); diff --git a/src/ol/format/topojsonformat.js b/src/ol/format/topojsonformat.js index 314b9b2c3e..5e272850d1 100644 --- a/src/ol/format/topojsonformat.js +++ b/src/ol/format/topojsonformat.js @@ -260,7 +260,7 @@ ol.format.TopoJSON.readFeatureFromGeometry_ = function(object, arcs, } var feature = new ol.Feature(); feature.setGeometry(ol.format.Feature.transformWithOptions( - geometry, false, opt_options)); + geometry, false, false, opt_options)); if (goog.isDef(object.id)) { feature.setId(object.id); } diff --git a/src/ol/format/xmlfeatureformat.js b/src/ol/format/xmlfeatureformat.js index f1d2eae3be..dc185b74df 100644 --- a/src/ol/format/xmlfeatureformat.js +++ b/src/ol/format/xmlfeatureformat.js @@ -5,6 +5,7 @@ goog.require('goog.asserts'); goog.require('goog.dom.NodeType'); goog.require('ol.format.Feature'); goog.require('ol.format.FormatType'); +goog.require('ol.proj'); goog.require('ol.xml'); @@ -243,3 +244,36 @@ ol.format.XMLFeature.prototype.writeGeometry = function(geometry, opt_options) { * @return {Node} Node. */ ol.format.XMLFeature.prototype.writeGeometryNode = goog.abstractMethod; + + +/** + * @param {Array.} features Features. + * @param {boolean} write Set to true for writing, false for reading. For + * writing, the features will be cloned before transforming. + * @param {(olx.format.WriteOptions|olx.format.ReadOptions)=} opt_options + * Options. + * @protected + * @return {Array.} Features. + */ +ol.format.XMLFeature.transformFeaturesWithOptions = function( + features, write, opt_options) { + var featureProjection = goog.isDef(opt_options) ? + ol.proj.get(opt_options.featureProjection) : null; + var dataProjection = goog.isDef(opt_options) ? + ol.proj.get(opt_options.dataProjection) : null; + + if (!goog.isNull(featureProjection) && !goog.isNull(dataProjection) && + !ol.proj.equivalent(featureProjection, dataProjection)) { + if (write) { + features = goog.array.map(features, function(feature) { + return feature.clone(); + }); + } + + goog.array.forEach(features, function(feature) { + feature.setGeometry(ol.format.Feature.transformWithOptions( + feature.getGeometry(), write, false, opt_options)); + }); + } + return features; +}; diff --git a/test/spec/ol/format/gpxformat.test.js b/test/spec/ol/format/gpxformat.test.js index 1a0f9c7180..0897b38c1d 100644 --- a/test/spec/ol/format/gpxformat.test.js +++ b/test/spec/ol/format/gpxformat.test.js @@ -1,5 +1,6 @@ goog.provide('ol.test.format.GPX'); +goog.require('ol.proj'); describe('ol.format.GPX', function() { @@ -80,6 +81,34 @@ describe('ol.format.GPX', function() { expect(serialized).to.xmleql(ol.xml.load(text)); }); + it('can transform, read and write a rte', function() { + var text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + var fs = format.readFeatures(text, { + featureProjection: 'EPSG:3857' + }); + 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); + 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(ol.geom.GeometryLayout.XYZM); + var serialized = format.writeFeatures(fs, { + featureProjection: 'EPSG:3857' + }); + expect(serialized).to.xmleql(ol.xml.load(text)); + }); + }); describe('trk', function() { @@ -181,6 +210,42 @@ describe('ol.format.GPX', function() { expect(serialized).to.xmleql(ol.xml.load(text)); }); + it('can tranform, read and write a trk with a trkseg', function() { + var text = + '' + + ' ' + + ' ' + + ' ' + + ' 3' + + ' ' + + ' ' + + ' ' + + ' 7' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + var fs = format.readFeatures(text, { + featureProjection: 'EPSG:3857' + }); + 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); + var p1 = ol.proj.transform([2, 1], 'EPSG:4326', 'EPSG:3857'); + p1.push(3, 1263115752); + var p2 = ol.proj.transform([6, 5], 'EPSG:4326', 'EPSG:3857'); + p2.push(7, 1263115812); + expect(g.getCoordinates()).to.eql([[p1, p2]]); + expect(g.getLayout()).to.be(ol.geom.GeometryLayout.XYZM); + var serialized = format.writeFeatures(fs, { + featureProjection: 'EPSG:3857' + }); + expect(serialized).to.xmleql(ol.xml.load(text)); + }); + it('can read and write a trk with multiple trksegs', function() { var text = '' + @@ -243,6 +308,29 @@ describe('ol.format.GPX', function() { expect(serialized).to.xmleql(ol.xml.load(text)); }); + it('can transform, read and write a wpt', function() { + var text = + '' + + ' ' + + ''; + var fs = format.readFeatures(text, { + featureProjection: 'EPSG:3857' + }); + 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); + 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(ol.geom.GeometryLayout.XYZM); + var serialized = format.writeFeatures(fs, { + featureProjection: 'EPSG:3857' + }); + expect(serialized).to.xmleql(ol.xml.load(text)); + }); + it('can read and write a wpt with ele', function() { var text = '' +