diff --git a/externs/olx.js b/externs/olx.js index 3ebe0f635f..0923b684e5 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -1632,7 +1632,8 @@ olx.format.ReadOptions.prototype.featureProjection; /** * @typedef {{dataProjection: ol.proj.ProjectionLike, * featureProjection: ol.proj.ProjectionLike, - * rightHanded: (boolean|undefined)}} + * rightHanded: (boolean|undefined), + * decimals: (number|undefined)}} * @api */ olx.format.WriteOptions; @@ -1675,6 +1676,20 @@ olx.format.WriteOptions.prototype.featureProjection; olx.format.WriteOptions.prototype.rightHanded; +/** + * Maximum number of decimal places for coordinates. Coordinates are stored + * internally as floats, but floating-point arithmetic can create coordinates + * with a large number of decimal places, not generally wanted on output. + * Set a number here to round coordinates. Can also be used to ensure that + * coordinates read in can be written back out with the same number of decimals. + * Default is no rounding. + * + * @type {number|undefined} + * @api + */ +olx.format.WriteOptions.prototype.decimals; + + /** * @typedef {{defaultDataProjection: ol.proj.ProjectionLike, * geometryName: (string|undefined)}} diff --git a/src/ol/format/featureformat.js b/src/ol/format/featureformat.js index c0bfafdaa9..43de17c15c 100644 --- a/src/ol/format/featureformat.js +++ b/src/ol/format/featureformat.js @@ -71,6 +71,9 @@ ol.format.Feature.prototype.adaptOptions = function(options) { options.dataProjection : this.defaultDataProjection, rightHanded: options.rightHanded }; + if (options.decimals) { + updatedOptions.decimals = options.decimals; + } } return updatedOptions; }; @@ -165,21 +168,45 @@ ol.format.Feature.transformWithOptions = function( ol.proj.get(opt_options.featureProjection) : null; var dataProjection = opt_options ? ol.proj.get(opt_options.dataProjection) : null; + /** + * @type {ol.geom.Geometry|ol.Extent} + */ + var transformed; if (featureProjection && dataProjection && !ol.proj.equivalent(featureProjection, dataProjection)) { if (geometry instanceof ol.geom.Geometry) { - return (write ? geometry.clone() : geometry).transform( + transformed = (write ? geometry.clone() : geometry).transform( write ? featureProjection : dataProjection, write ? dataProjection : featureProjection); } else { // FIXME this is necessary because ol.format.GML treats extents // as geometries - return ol.proj.transformExtent( + transformed = ol.proj.transformExtent( write ? geometry.slice() : geometry, write ? featureProjection : dataProjection, write ? dataProjection : featureProjection); } } else { - return geometry; + transformed = geometry; } + if (write && opt_options && opt_options.decimals) { + var power = Math.pow(10, opt_options.decimals); + // if decimals option on write, round each coordinate appropriately + /** + * @param {Array.} coordinates Coordinates. + * @return {Array.} Transformed coordinates. + */ + var transform = function(coordinates) { + for (var i = 0, ii = coordinates.length; i < ii; ++i) { + coordinates[i] = Math.round(coordinates[i] * power) / power; + } + return coordinates; + }; + if (Array.isArray(transformed)) { + transform(/** @type {ol.Extent} */ (transformed)); + } else { + transformed.applyTransform(transform); + } + } + return transformed; }; diff --git a/test/spec/ol/format/geojsonformat.test.js b/test/spec/ol/format/geojsonformat.test.js index d0a9dc264c..2fb209971a 100644 --- a/test/spec/ol/format/geojsonformat.test.js +++ b/test/spec/ol/format/geojsonformat.test.js @@ -746,6 +746,26 @@ describe('ol.format.GeoJSON', function() { expect(geometries[1].getCoordinates()[0][1]).to.roughlyEqual( gotGeometries[1].getCoordinates()[0][1], 1e-8); }); + + it('truncates transformed point with decimals option', function() { + var point = new ol.geom.Point([2, 3]).transform('EPSG:4326','EPSG:3857'); + var geojson = format.writeGeometry(point, { + featureProjection: 'EPSG:3857', + decimals: 2 + }); + expect(format.readGeometry(geojson).getCoordinates()).to.eql( + [2, 3]); + }); + + it('truncates a linestring with decimals option', function() { + var linestring = new ol.geom.LineString([[42.123456789, 38.987654321], + [43, 39]]); + var geojson = format.writeGeometry(linestring, { + decimals: 6 + }); + expect(format.readGeometry(geojson).getCoordinates()).to.eql( + [[42.123457, 38.987654], [43, 39]]); + }); }); });