From 0df692e159a57593028eb2b114380e64b39ae801 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 3 Mar 2013 17:57:30 +0100 Subject: [PATCH 1/4] Lint in the tests --- test/spec/ol/extent.test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index e3f0f4a321..11431db798 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -52,17 +52,17 @@ describe('ol.Extent', function() { expect(extent.containsCoordinate( new ol.Coordinate(3, 1))).toBeFalsy(); expect(extent.containsCoordinate( - new ol.Coordinate(3, 5))).toBeFalsy(); + new ol.Coordinate(3, 5))).toBeFalsy(); expect(extent.containsCoordinate( - new ol.Coordinate(4, 1))).toBeFalsy(); + new ol.Coordinate(4, 1))).toBeFalsy(); expect(extent.containsCoordinate( - new ol.Coordinate(4, 2))).toBeFalsy(); + new ol.Coordinate(4, 2))).toBeFalsy(); expect(extent.containsCoordinate( - new ol.Coordinate(4, 3))).toBeFalsy(); + new ol.Coordinate(4, 3))).toBeFalsy(); expect(extent.containsCoordinate( - new ol.Coordinate(4, 4))).toBeFalsy(); + new ol.Coordinate(4, 4))).toBeFalsy(); expect(extent.containsCoordinate( - new ol.Coordinate(4, 5))).toBeFalsy(); + new ol.Coordinate(4, 5))).toBeFalsy(); }); }); }); From 7a5e6a06d8d957b8170b306b011f7857c6fb297e Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 3 Mar 2013 19:27:57 +0100 Subject: [PATCH 2/4] Make transform functions work with arrays Instead of working with ol.Coordinate instances, transform functions work with arrays. This is in anticipation of using transform functions to transform large arrays of vertex coordinate values. The ol.projection.transform and ol.projection.transformWithCodes are slightly more convenient functions for dealing with ol.Coordinate instances. Whether we make this more consistent with the use of functions returned by ol.projection.getTransform is up for discussion. --- src/ol/control/mousepositioncontrol.js | 4 +- src/ol/extent.js | 10 ++-- src/ol/geolocation.js | 15 +++--- src/ol/projection.js | 56 ++++++++++++++------- src/ol/projection/epsg3857.js | 55 +++++++++++++++------ src/ol/transformfunction.js | 8 +-- test/spec/ol/extent.test.js | 4 +- test/spec/ol/projection.test.js | 68 +++++++++++++++++++++++--- 8 files changed, 163 insertions(+), 57 deletions(-) diff --git a/src/ol/control/mousepositioncontrol.js b/src/ol/control/mousepositioncontrol.js index 09abf92d4b..0f3cfb9400 100644 --- a/src/ol/control/mousepositioncontrol.js +++ b/src/ol/control/mousepositioncontrol.js @@ -8,6 +8,7 @@ goog.require('goog.dom'); goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('goog.style'); +goog.require('ol.Coordinate'); goog.require('ol.CoordinateFormatType'); goog.require('ol.MapEvent'); goog.require('ol.MapEventType'); @@ -177,7 +178,8 @@ ol.control.MousePosition.prototype.updateHTML_ = function(pixel) { var map = this.getMap(); var coordinate = map.getCoordinateFromPixel(pixel); if (!goog.isNull(coordinate)) { - coordinate = this.transform_(coordinate); + var output = this.transform_([coordinate.x, coordinate.y]); + coordinate = new ol.Coordinate(output[0], output[1]); if (goog.isDef(this.coordinateFormat_)) { html = this.coordinateFormat_(coordinate); } else { diff --git a/src/ol/extent.js b/src/ol/extent.js index 3606dfc63c..0edfa2a8ca 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -108,8 +108,10 @@ ol.Extent.prototype.getTopRight = function() { * @return {ol.Extent} Extent. */ ol.Extent.prototype.transform = function(transformFn) { - var a = transformFn(new ol.Coordinate(this.minX, this.minY)); - var b = transformFn(new ol.Coordinate(this.maxX, this.maxY)); - return new ol.Extent(Math.min(a.x, b.x), Math.min(a.y, b.y), - Math.max(a.x, b.x), Math.max(a.y, b.y)); + var input = [this.minX, this.minY, this.maxX, this.maxY]; + var output = transformFn(input, 2); + return new ol.Extent(Math.min(output[0], output[2]), + Math.min(output[1], output[3]), + Math.max(output[0], output[2]), + Math.max(output[1], output[3])); }; diff --git a/src/ol/geolocation.js b/src/ol/geolocation.js index 201257e2eb..69052832b2 100644 --- a/src/ol/geolocation.js +++ b/src/ol/geolocation.js @@ -76,11 +76,12 @@ ol.Geolocation.prototype.disposeInternal = function() { ol.Geolocation.prototype.handleProjectionChanged_ = function() { var projection = this.getProjection(); if (goog.isDefAndNotNull(projection)) { - this.transformCoords_ = ol.projection.getTransform( + this.transformFn_ = ol.projection.getTransform( ol.projection.getFromCode('EPSG:4326'), projection); if (!goog.isNull(this.position_)) { + var output = this.transformFn_([this.position_.x, this.position_.y]); this.set(ol.GeolocationProperty.POSITION, - this.transformCoords_(this.position_)); + new ol.Coordinate(output[0], output[1])); } } }; @@ -108,8 +109,9 @@ ol.Geolocation.prototype.positionChange_ = function(position) { this.set(ol.GeolocationProperty.HEADING, goog.isNull(coords.heading) ? undefined : goog.math.toRadians(coords.heading)); this.position_ = new ol.Coordinate(coords.longitude, coords.latitude); + var output = this.transformFn_([coords.longitude, coords.latitude]); this.set(ol.GeolocationProperty.POSITION, - this.transformCoords_(this.position_)); + new ol.Coordinate(output[0], output[1])); this.set(ol.GeolocationProperty.SPEED, goog.isNull(coords.speed) ? undefined : coords.speed); }; @@ -230,7 +232,8 @@ goog.exportProperty( /** * @private - * @param {ol.Coordinate} coordinate Coordinate. - * @return {ol.Coordinate} Coordinate. + * @param {Array.} input Input coordinate values. + * @param {number=} opt_dimension Dimension (default is 2). + * @return {Array.} Output coordinate values. */ -ol.Geolocation.prototype.transformCoords_ = goog.functions.identity; +ol.Geolocation.prototype.transformFn_ = goog.functions.identity; diff --git a/src/ol/projection.js b/src/ol/projection.js index 6c1e321f14..80e8c1dbe2 100644 --- a/src/ol/projection.js +++ b/src/ol/projection.js @@ -397,14 +397,29 @@ ol.projection.getTransform = function(source, destination) { var destinationProj4jsProj = proj4jsDestination.getProj4jsProj(); transform = /** - * @param {ol.Coordinate} coordinate Coordinate. - * @return {ol.Coordinate} Coordinate. + * @param {Array.} input Input coordinate values. + * @param {number=} opt_dimension Dimension. + * @return {Array.} Output coordinate values. */ - function(coordinate) { - var proj4jsPoint = new Proj4js.Point(coordinate.x, coordinate.y); - proj4jsPoint = Proj4js.transform( - sourceProj4jsProj, destinationProj4jsProj, proj4jsPoint); - return new ol.Coordinate(proj4jsPoint.x, proj4jsPoint.y); + function(input, opt_dimension) { + var length = input.length, + dimension = goog.isDef(opt_dimension) ? opt_dimension : 2, + output, proj4jsPoint; + if (dimension > 2) { + // preserve values beyond second dimension + output = input.slice(); + } else { + output = new Array(length); + } + goog.asserts.assert(output.length % dimension === 0); + for (var i = 0; i < length; i += dimension) { + proj4jsPoint = new Proj4js.Point(input[i], input[i + 1]); + proj4jsPoint = Proj4js.transform( + sourceProj4jsProj, destinationProj4jsProj, proj4jsPoint); + output[i] = proj4jsPoint.x; + output[i + 1] = proj4jsPoint.y; + } + return output; }; ol.projection.addTransform(source, destination, transform); } @@ -418,7 +433,7 @@ ol.projection.getTransform = function(source, destination) { /** * Given the projection codes this method searches for a transformation function - * to convert coordinate from the source projection to the destination + * to convert a coordinates array from the source projection to the destination * projection. * * @param {string} sourceCode Source code. @@ -433,20 +448,23 @@ ol.projection.getTransformFromCodes = function(sourceCode, destinationCode) { /** - * @param {ol.Coordinate} point Point. - * @return {ol.Coordinate} Unaltered point (same reference). + * @param {Array.} input Input coordinate array. + * @param {number=} opt_dimension Dimension. + * @return {Array.} Input coordinate array (same array as input). */ -ol.projection.identityTransform = function(point) { - return point; +ol.projection.identityTransform = function(input, opt_dimension) { + return input; }; /** - * @param {ol.Coordinate} point Point. - * @return {ol.Coordinate} Equal point (different reference). + * @param {Array.} input Input coordinate array. + * @param {number=} opt_dimension Dimension. + * @return {Array.} Output coordinate array (new array, same coordinate + * values). */ -ol.projection.cloneTransform = function(point) { - return new ol.Coordinate(point.x, point.y); +ol.projection.cloneTransform = function(input, opt_dimension) { + return input.slice(); }; @@ -460,7 +478,8 @@ ol.projection.cloneTransform = function(point) { */ ol.projection.transform = function(point, source, destination) { var transformFn = ol.projection.getTransform(source, destination); - return transformFn(point); + var output = transformFn([point.x, point.y]); + return new ol.Coordinate(output[0], output[1]); }; @@ -474,5 +493,6 @@ ol.projection.transformWithCodes = function(point, sourceCode, destinationCode) { var transformFn = ol.projection.getTransformFromCodes( sourceCode, destinationCode); - return transformFn(point); + var output = transformFn([point.x, point.y]); + return new ol.Coordinate(output[0], output[1]); }; diff --git a/src/ol/projection/epsg3857.js b/src/ol/projection/epsg3857.js index 991eca5410..2050aca7d2 100644 --- a/src/ol/projection/epsg3857.js +++ b/src/ol/projection/epsg3857.js @@ -1,7 +1,6 @@ goog.provide('ol.projection.EPSG3857'); goog.require('goog.array'); -goog.require('ol.Coordinate'); goog.require('ol.Extent'); goog.require('ol.Projection'); goog.require('ol.ProjectionUnits'); @@ -73,26 +72,52 @@ ol.projection.EPSG3857.PROJECTIONS = goog.array.map( /** * Transformation from EPSG:4326 to EPSG:3857. * - * @param {ol.Coordinate} point Point. - * @return {ol.Coordinate} Point. + * @param {Array.} input Input array of coordinate values. + * @param {number=} opt_dimension Dimension (default is 2). + * @return {Array.} Output array of coordinate values. */ -ol.projection.EPSG3857.fromEPSG4326 = function(point) { - var x = ol.projection.EPSG3857.RADIUS * Math.PI * point.x / 180; - var y = ol.projection.EPSG3857.RADIUS * - Math.log(Math.tan(Math.PI * (point.y + 90) / 360)); - return new ol.Coordinate(x, y); +ol.projection.EPSG3857.fromEPSG4326 = function(input, opt_dimension) { + var length = input.length, + dimension = goog.isDef(opt_dimension) ? opt_dimension : 2, + output; + if (dimension > 2) { + // preserve values beyond second dimension + output = input.slice(); + } else { + output = new Array(length); + } + goog.asserts.assert(output.length % dimension === 0); + for (var i = 0; i < length; i += dimension) { + output[i] = ol.projection.EPSG3857.RADIUS * Math.PI * input[i] / 180; + output[i + 1] = ol.projection.EPSG3857.RADIUS * + Math.log(Math.tan(Math.PI * (input[i + 1] + 90) / 360)); + } + return output; }; /** * Transformation from EPSG:3857 to EPSG:4326. * - * @param {ol.Coordinate} point Point. - * @return {ol.Coordinate} Point. + * @param {Array.} input Input array of coordinate values. + * @param {number=} opt_dimension Dimension (default is 2). + * @return {Array.} Output array of coordinate values. */ -ol.projection.EPSG3857.toEPSG4326 = function(point) { - var x = 180 * point.x / (ol.projection.EPSG3857.RADIUS * Math.PI); - var y = 360 * Math.atan( - Math.exp(point.y / ol.projection.EPSG3857.RADIUS)) / Math.PI - 90; - return new ol.Coordinate(x, y); +ol.projection.EPSG3857.toEPSG4326 = function(input, opt_dimension) { + var length = input.length, + dimension = goog.isDef(opt_dimension) ? opt_dimension : 2, + output; + if (dimension > 2) { + // preserve values beyond second dimension + output = input.slice(); + } else { + output = new Array(length); + } + goog.asserts.assert(output.length % dimension === 0); + for (var i = 0; i < length; i += dimension) { + output[i] = 180 * input[i] / (ol.projection.EPSG3857.RADIUS * Math.PI); + output[i + 1] = 360 * Math.atan( + Math.exp(input[i + 1] / ol.projection.EPSG3857.RADIUS)) / Math.PI - 90; + } + return output; }; diff --git a/src/ol/transformfunction.js b/src/ol/transformfunction.js index a8be2c0f28..65b5a6c585 100644 --- a/src/ol/transformfunction.js +++ b/src/ol/transformfunction.js @@ -1,9 +1,11 @@ goog.provide('ol.TransformFunction'); -goog.require('ol.Coordinate'); - /** - * @typedef {function(ol.Coordinate): ol.Coordinate} + * A transform function accepts an array of input coordinate values and an + * optional dimension (default should be 2). The function transforms the + * coordinate values and returns an array of the same length as the input. + * + * @typedef {function(Array., number=): Array.} */ ol.TransformFunction; diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index 11431db798..9cd1ba3c40 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -84,8 +84,8 @@ describe('ol.Extent', function() { }); it('takes arbitrary function', function() { - var transformFn = function(coordinate) { - return new ol.Coordinate(-coordinate.x, -coordinate.y); + var transformFn = function(input) { + return [-input[0], -input[1], -input[2], -input[3]]; }; var sourceExtent = new ol.Extent(-15, -30, 45, 60); var destinationExtent = sourceExtent.transform(transformFn); diff --git a/test/spec/ol/projection.test.js b/test/spec/ol/projection.test.js index 42b3f09fc2..a840a6f0f4 100644 --- a/test/spec/ol/projection.test.js +++ b/test/spec/ol/projection.test.js @@ -155,10 +155,22 @@ describe('ol.projection', function() { var transform = ol.projection.getTransform(sm, gg); expect(typeof transform).toBe('function'); - var coordinate = transform(new ol.Coordinate(-12000000, 5000000)); + var output = transform([-12000000, 5000000]); - expect(coordinate.x).toRoughlyEqual(-107.79783409434258, 1e-9); - expect(coordinate.y).toRoughlyEqual(40.91627447067577, 1e-9); + expect(output[0]).toRoughlyEqual(-107.79783409434258, 1e-9); + expect(output[1]).toRoughlyEqual(40.91627447067577, 1e-9); + }); + + it('works for longer arrays', function() { + var transform = ol.projection.getTransform(sm, gg); + expect(typeof transform).toBe('function'); + + var output = transform([-12000000, 5000000, -12000000, 5000000]); + + expect(output[0]).toRoughlyEqual(-107.79783409434258, 1e-9); + expect(output[1]).toRoughlyEqual(40.91627447067577, 1e-9); + expect(output[2]).toRoughlyEqual(-107.79783409434258, 1e-9); + expect(output[3]).toRoughlyEqual(40.91627447067577, 1e-9); }); }); @@ -177,14 +189,54 @@ describe('ol.projection', function() { 'GOOGLE', 'EPSG:4326'); expect(typeof transform).toBe('function'); - var coordinate = transform( - new ol.Coordinate(-626172.13571216376, 6887893.4928337997)); + var output = transform([-626172.13571216376, 6887893.4928337997]); - expect(coordinate.x).toRoughlyEqual(-5.625, 1e-9); - expect(coordinate.y).toRoughlyEqual(52.4827802220782, 1e-9); + expect(output[0]).toRoughlyEqual(-5.625, 1e-9); + expect(output[1]).toRoughlyEqual(52.4827802220782, 1e-9); }); + it('works for longer arrays of coordinate values', function() { + var transform = ol.projection.getTransformFromCodes( + 'GOOGLE', 'EPSG:4326'); + expect(typeof transform).toBe('function'); + + var output = transform([ + -626172.13571216376, 6887893.4928337997, + -12000000, 5000000, + -626172.13571216376, 6887893.4928337997 + ]); + + expect(output[0]).toRoughlyEqual(-5.625, 1e-9); + expect(output[1]).toRoughlyEqual(52.4827802220782, 1e-9); + expect(output[2]).toRoughlyEqual(-107.79783409434258, 1e-9); + expect(output[3]).toRoughlyEqual(40.91627447067577, 1e-9); + expect(output[4]).toRoughlyEqual(-5.625, 1e-9); + expect(output[5]).toRoughlyEqual(52.4827802220782, 1e-9); + }); + + it('accepts a dimension', function() { + var transform = ol.projection.getTransformFromCodes( + 'GOOGLE', 'EPSG:4326'); + expect(typeof transform).toBe('function'); + + var dimension = 3; + var output = transform([ + -626172.13571216376, 6887893.4928337997, 100, + -12000000, 5000000, 200, + -626172.13571216376, 6887893.4928337997, 300 + ], dimension); + + expect(output[0]).toRoughlyEqual(-5.625, 1e-9); + expect(output[1]).toRoughlyEqual(52.4827802220782, 1e-9); + expect(output[2]).toBe(100); + expect(output[3]).toRoughlyEqual(-107.79783409434258, 1e-9); + expect(output[4]).toRoughlyEqual(40.91627447067577, 1e-9); + expect(output[5]).toBe(200); + expect(output[6]).toRoughlyEqual(-5.625, 1e-9); + expect(output[7]).toRoughlyEqual(52.4827802220782, 1e-9); + expect(output[8]).toBe(300); + }); }); describe('ol.projection.removeTransform()', function() { @@ -195,7 +247,7 @@ describe('ol.projection', function() { it('removes functions cached by addTransform', function() { var foo = new ol.Projection('foo', units, extent); var bar = new ol.Projection('bar', units, extent); - var transform = function() {}; + var transform = function(intput, dimension) {return input}; ol.projection.addTransform(foo, bar, transform); expect(ol.projection.transforms_).not.toBeUndefined(); expect(ol.projection.transforms_.foo).not.toBeUndefined(); From 40bde4056b76cb2fe8000619fe95de51fa3152c3 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 3 Mar 2013 20:24:14 +0100 Subject: [PATCH 3/4] Accept a destination array for transforms This allows in-place transforms. --- src/ol/extent.js | 10 ++++----- src/ol/geolocation.js | 1 + src/ol/projection.js | 26 +++++++++++++--------- src/ol/projection/epsg3857.js | 39 +++++++++++++++++++-------------- src/ol/transformfunction.js | 9 ++++---- test/spec/ol/projection.test.js | 19 ++++++++++++++-- 6 files changed, 67 insertions(+), 37 deletions(-) diff --git a/src/ol/extent.js b/src/ol/extent.js index 0edfa2a8ca..5595382e7c 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -109,9 +109,9 @@ ol.Extent.prototype.getTopRight = function() { */ ol.Extent.prototype.transform = function(transformFn) { var input = [this.minX, this.minY, this.maxX, this.maxY]; - var output = transformFn(input, 2); - return new ol.Extent(Math.min(output[0], output[2]), - Math.min(output[1], output[3]), - Math.max(output[0], output[2]), - Math.max(output[1], output[3])); + input = transformFn(input, input, 2); + return new ol.Extent(Math.min(input[0], input[2]), + Math.min(input[1], input[3]), + Math.max(input[0], input[2]), + Math.max(input[1], input[3])); }; diff --git a/src/ol/geolocation.js b/src/ol/geolocation.js index 69052832b2..bb90723f15 100644 --- a/src/ol/geolocation.js +++ b/src/ol/geolocation.js @@ -233,6 +233,7 @@ goog.exportProperty( /** * @private * @param {Array.} input Input coordinate values. + * @param {Array.=} opt_output Output array of coordinate values. * @param {number=} opt_dimension Dimension (default is 2). * @return {Array.} Output coordinate values. */ diff --git a/src/ol/projection.js b/src/ol/projection.js index 80e8c1dbe2..86ac70b6a5 100644 --- a/src/ol/projection.js +++ b/src/ol/projection.js @@ -398,20 +398,24 @@ ol.projection.getTransform = function(source, destination) { transform = /** * @param {Array.} input Input coordinate values. + * @param {Array.=} opt_output Output array of coordinates. * @param {number=} opt_dimension Dimension. * @return {Array.} Output coordinate values. */ - function(input, opt_dimension) { + function(input, opt_output, opt_dimension) { var length = input.length, - dimension = goog.isDef(opt_dimension) ? opt_dimension : 2, - output, proj4jsPoint; - if (dimension > 2) { - // preserve values beyond second dimension - output = input.slice(); - } else { - output = new Array(length); + dimension = opt_dimension > 1 ? opt_dimension : 2, + output = opt_output; + if (!goog.isDef(output)) { + if (dimension > 2) { + // preserve values beyond second dimension + output = input.slice(); + } else { + output = new Array(length); + } } goog.asserts.assert(output.length % dimension === 0); + var proj4jsPoint; for (var i = 0; i < length; i += dimension) { proj4jsPoint = new Proj4js.Point(input[i], input[i + 1]); proj4jsPoint = Proj4js.transform( @@ -449,21 +453,23 @@ ol.projection.getTransformFromCodes = function(sourceCode, destinationCode) { /** * @param {Array.} input Input coordinate array. + * @param {Array.=} opt_output Output array of coordinate values. * @param {number=} opt_dimension Dimension. * @return {Array.} Input coordinate array (same array as input). */ -ol.projection.identityTransform = function(input, opt_dimension) { +ol.projection.identityTransform = function(input, opt_output, opt_dimension) { return input; }; /** * @param {Array.} input Input coordinate array. + * @param {Array.=} opt_output Output array of coordinate values. * @param {number=} opt_dimension Dimension. * @return {Array.} Output coordinate array (new array, same coordinate * values). */ -ol.projection.cloneTransform = function(input, opt_dimension) { +ol.projection.cloneTransform = function(input, opt_output, opt_dimension) { return input.slice(); }; diff --git a/src/ol/projection/epsg3857.js b/src/ol/projection/epsg3857.js index 2050aca7d2..0d183c3e66 100644 --- a/src/ol/projection/epsg3857.js +++ b/src/ol/projection/epsg3857.js @@ -73,18 +73,22 @@ ol.projection.EPSG3857.PROJECTIONS = goog.array.map( * Transformation from EPSG:4326 to EPSG:3857. * * @param {Array.} input Input array of coordinate values. + * @param {Array.=} opt_output Output array of coordinate values. * @param {number=} opt_dimension Dimension (default is 2). * @return {Array.} Output array of coordinate values. */ -ol.projection.EPSG3857.fromEPSG4326 = function(input, opt_dimension) { +ol.projection.EPSG3857.fromEPSG4326 = function( + input, opt_output, opt_dimension) { var length = input.length, - dimension = goog.isDef(opt_dimension) ? opt_dimension : 2, - output; - if (dimension > 2) { - // preserve values beyond second dimension - output = input.slice(); - } else { - output = new Array(length); + dimension = opt_dimension > 1 ? opt_dimension : 2, + output = opt_output; + if (!goog.isDef(output)) { + if (dimension > 2) { + // preserve values beyond second dimension + output = input.slice(); + } else { + output = new Array(length); + } } goog.asserts.assert(output.length % dimension === 0); for (var i = 0; i < length; i += dimension) { @@ -100,18 +104,21 @@ ol.projection.EPSG3857.fromEPSG4326 = function(input, opt_dimension) { * Transformation from EPSG:3857 to EPSG:4326. * * @param {Array.} input Input array of coordinate values. + * @param {Array.=} opt_output Output array of coordinate values. * @param {number=} opt_dimension Dimension (default is 2). * @return {Array.} Output array of coordinate values. */ -ol.projection.EPSG3857.toEPSG4326 = function(input, opt_dimension) { +ol.projection.EPSG3857.toEPSG4326 = function(input, opt_output, opt_dimension) { var length = input.length, - dimension = goog.isDef(opt_dimension) ? opt_dimension : 2, - output; - if (dimension > 2) { - // preserve values beyond second dimension - output = input.slice(); - } else { - output = new Array(length); + dimension = opt_dimension > 1 ? opt_dimension : 2, + output = opt_output; + if (!goog.isDef(output)) { + if (dimension > 2) { + // preserve values beyond second dimension + output = input.slice(); + } else { + output = new Array(length); + } } goog.asserts.assert(output.length % dimension === 0); for (var i = 0; i < length; i += dimension) { diff --git a/src/ol/transformfunction.js b/src/ol/transformfunction.js index 65b5a6c585..b5fb315d69 100644 --- a/src/ol/transformfunction.js +++ b/src/ol/transformfunction.js @@ -2,10 +2,11 @@ goog.provide('ol.TransformFunction'); /** - * A transform function accepts an array of input coordinate values and an - * optional dimension (default should be 2). The function transforms the - * coordinate values and returns an array of the same length as the input. + * A transform function accepts an array of input coordinate values, an optional + * output array, and an optional dimension (default should be 2). The function + * transforms the input coordinate values, populates the output array, and + * returns the output array. * - * @typedef {function(Array., number=): Array.} + * @typedef {function(Array., Array.=, number=): Array.} */ ol.TransformFunction; diff --git a/test/spec/ol/projection.test.js b/test/spec/ol/projection.test.js index a840a6f0f4..5a217e0f40 100644 --- a/test/spec/ol/projection.test.js +++ b/test/spec/ol/projection.test.js @@ -215,6 +215,21 @@ describe('ol.projection', function() { expect(output[5]).toRoughlyEqual(52.4827802220782, 1e-9); }); + it('accepts an optional destination array', function() { + var transform = ol.projection.getTransformFromCodes( + 'EPSG:3857', 'EPSG:4326'); + var input = [-12000000, 5000000]; + var output = []; + + var got = transform(input, output); + expect(got).toBe(output); + + expect(output[0]).toRoughlyEqual(-107.79783409434258, 1e-9); + expect(output[1]).toRoughlyEqual(40.91627447067577, 1e-9); + + expect(input).toEqual([-12000000, 5000000]); + }); + it('accepts a dimension', function() { var transform = ol.projection.getTransformFromCodes( 'GOOGLE', 'EPSG:4326'); @@ -225,7 +240,7 @@ describe('ol.projection', function() { -626172.13571216376, 6887893.4928337997, 100, -12000000, 5000000, 200, -626172.13571216376, 6887893.4928337997, 300 - ], dimension); + ], undefined, dimension); expect(output[0]).toRoughlyEqual(-5.625, 1e-9); expect(output[1]).toRoughlyEqual(52.4827802220782, 1e-9); @@ -247,7 +262,7 @@ describe('ol.projection', function() { it('removes functions cached by addTransform', function() { var foo = new ol.Projection('foo', units, extent); var bar = new ol.Projection('bar', units, extent); - var transform = function(intput, dimension) {return input}; + var transform = function(input, output, dimension) {return input}; ol.projection.addTransform(foo, bar, transform); expect(ol.projection.transforms_).not.toBeUndefined(); expect(ol.projection.transforms_.foo).not.toBeUndefined(); From e9fa08fccb436184de499a367f7bd13a8239f8bf Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 3 Mar 2013 20:53:13 +0100 Subject: [PATCH 4/4] Use in-place transforms where possible --- src/ol/control/mousepositioncontrol.js | 5 +++-- src/ol/geolocation.js | 10 ++++++---- src/ol/projection.js | 19 ++++++++++++++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/ol/control/mousepositioncontrol.js b/src/ol/control/mousepositioncontrol.js index 0f3cfb9400..60a667f4a0 100644 --- a/src/ol/control/mousepositioncontrol.js +++ b/src/ol/control/mousepositioncontrol.js @@ -178,8 +178,9 @@ ol.control.MousePosition.prototype.updateHTML_ = function(pixel) { var map = this.getMap(); var coordinate = map.getCoordinateFromPixel(pixel); if (!goog.isNull(coordinate)) { - var output = this.transform_([coordinate.x, coordinate.y]); - coordinate = new ol.Coordinate(output[0], output[1]); + var vertex = [coordinate.x, coordinate.y]; + vertex = this.transform_(vertex, vertex); + coordinate = new ol.Coordinate(vertex[0], vertex[1]); if (goog.isDef(this.coordinateFormat_)) { html = this.coordinateFormat_(coordinate); } else { diff --git a/src/ol/geolocation.js b/src/ol/geolocation.js index bb90723f15..5920f92328 100644 --- a/src/ol/geolocation.js +++ b/src/ol/geolocation.js @@ -79,9 +79,10 @@ ol.Geolocation.prototype.handleProjectionChanged_ = function() { this.transformFn_ = ol.projection.getTransform( ol.projection.getFromCode('EPSG:4326'), projection); if (!goog.isNull(this.position_)) { - var output = this.transformFn_([this.position_.x, this.position_.y]); + var vertex = [this.position_.x, this.position_.y]; + vertex = this.transformFn_(vertex, vertex, 2); this.set(ol.GeolocationProperty.POSITION, - new ol.Coordinate(output[0], output[1])); + new ol.Coordinate(vertex[0], vertex[1])); } } }; @@ -109,9 +110,10 @@ ol.Geolocation.prototype.positionChange_ = function(position) { this.set(ol.GeolocationProperty.HEADING, goog.isNull(coords.heading) ? undefined : goog.math.toRadians(coords.heading)); this.position_ = new ol.Coordinate(coords.longitude, coords.latitude); - var output = this.transformFn_([coords.longitude, coords.latitude]); + var vertex = [coords.longitude, coords.latitude]; + vertex = this.transformFn_(vertex, vertex, 2); this.set(ol.GeolocationProperty.POSITION, - new ol.Coordinate(output[0], output[1])); + new ol.Coordinate(vertex[0], vertex[1])); this.set(ol.GeolocationProperty.SPEED, goog.isNull(coords.speed) ? undefined : coords.speed); }; diff --git a/src/ol/projection.js b/src/ol/projection.js index 86ac70b6a5..30cc5deb2c 100644 --- a/src/ol/projection.js +++ b/src/ol/projection.js @@ -458,6 +458,14 @@ ol.projection.getTransformFromCodes = function(sourceCode, destinationCode) { * @return {Array.} Input coordinate array (same array as input). */ ol.projection.identityTransform = function(input, opt_output, opt_dimension) { + if (goog.isDef(opt_output) && input !== opt_output) { + // TODO: consider making this a warning instead + goog.asserts.assert(false, 'This should not be used internally.'); + for (var i = 0, ii = input.length; i < ii; ++i) { + opt_output[i] = input[i]; + } + input = opt_output; + } return input; }; @@ -470,7 +478,16 @@ ol.projection.identityTransform = function(input, opt_output, opt_dimension) { * values). */ ol.projection.cloneTransform = function(input, opt_output, opt_dimension) { - return input.slice(); + var output; + if (goog.isDef(opt_output)) { + for (var i = 0, ii = input.length; i < ii; ++i) { + opt_output[i] = input[i]; + } + output = opt_output; + } else { + output = input.slice(); + } + return output; };