diff --git a/src/ol/control/mousepositioncontrol.js b/src/ol/control/mousepositioncontrol.js index 09abf92d4b..60a667f4a0 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,9 @@ 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 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/extent.js b/src/ol/extent.js index 3606dfc63c..5595382e7c 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]; + 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 201257e2eb..5920f92328 100644 --- a/src/ol/geolocation.js +++ b/src/ol/geolocation.js @@ -76,11 +76,13 @@ 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 vertex = [this.position_.x, this.position_.y]; + vertex = this.transformFn_(vertex, vertex, 2); this.set(ol.GeolocationProperty.POSITION, - this.transformCoords_(this.position_)); + new ol.Coordinate(vertex[0], vertex[1])); } } }; @@ -108,8 +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 vertex = [coords.longitude, coords.latitude]; + vertex = this.transformFn_(vertex, vertex, 2); this.set(ol.GeolocationProperty.POSITION, - this.transformCoords_(this.position_)); + new ol.Coordinate(vertex[0], vertex[1])); this.set(ol.GeolocationProperty.SPEED, goog.isNull(coords.speed) ? undefined : coords.speed); }; @@ -230,7 +234,9 @@ goog.exportProperty( /** * @private - * @param {ol.Coordinate} coordinate Coordinate. - * @return {ol.Coordinate} Coordinate. + * @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. */ -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..30cc5deb2c 100644 --- a/src/ol/projection.js +++ b/src/ol/projection.js @@ -397,14 +397,33 @@ 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 {Array.=} opt_output Output array of coordinates. + * @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_output, opt_dimension) { + var length = input.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( + sourceProj4jsProj, destinationProj4jsProj, proj4jsPoint); + output[i] = proj4jsPoint.x; + output[i + 1] = proj4jsPoint.y; + } + return output; }; ol.projection.addTransform(source, destination, transform); } @@ -418,7 +437,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 +452,42 @@ 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 {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(point) { - return point; +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; }; /** - * @param {ol.Coordinate} point Point. - * @return {ol.Coordinate} Equal point (different reference). + * @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(point) { - return new ol.Coordinate(point.x, point.y); +ol.projection.cloneTransform = function(input, opt_output, opt_dimension) { + 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; }; @@ -460,7 +501,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 +516,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..0d183c3e66 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,59 @@ 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 {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(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_output, opt_dimension) { + var length = input.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) { + 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 {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(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_output, opt_dimension) { + var length = input.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) { + 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..b5fb315d69 100644 --- a/src/ol/transformfunction.js +++ b/src/ol/transformfunction.js @@ -1,9 +1,12 @@ goog.provide('ol.TransformFunction'); -goog.require('ol.Coordinate'); - /** - * @typedef {function(ol.Coordinate): ol.Coordinate} + * 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., Array.=, number=): Array.} */ ol.TransformFunction; diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index e3f0f4a321..9cd1ba3c40 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(); }); }); }); @@ -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..5a217e0f40 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,69 @@ 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 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'); + expect(typeof transform).toBe('function'); + + var dimension = 3; + var output = transform([ + -626172.13571216376, 6887893.4928337997, 100, + -12000000, 5000000, 200, + -626172.13571216376, 6887893.4928337997, 300 + ], undefined, 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 +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() {}; + 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();