From 684ea356733afac752a04213e56f928b5148eeca Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 3 Mar 2013 16:17:38 +0100 Subject: [PATCH 1/6] Add sphere with radius equal to the semi-major axis of the WGS84 ellipsoid --- src/ol/sphere/wgs84.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/ol/sphere/wgs84.js diff --git a/src/ol/sphere/wgs84.js b/src/ol/sphere/wgs84.js new file mode 100644 index 0000000000..e30aafeb4b --- /dev/null +++ b/src/ol/sphere/wgs84.js @@ -0,0 +1,10 @@ +goog.provide('ol.sphere.WGS84'); + +goog.require('ol.Sphere'); + + +/** + * A sphere with radius equal to the semi-major axis of the WGS84 ellipsoid. + * @const {ol.Sphere} + */ +ol.sphere.WGS84 = new ol.Sphere(6378137); From e6f08df72caaeb3b5ebf306a7ee81a0abe076cc3 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 3 Mar 2013 17:14:35 +0100 Subject: [PATCH 2/6] Add the normal sphere --- src/ol/sphere/normal.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/ol/sphere/normal.js diff --git a/src/ol/sphere/normal.js b/src/ol/sphere/normal.js new file mode 100644 index 0000000000..e07bf000b4 --- /dev/null +++ b/src/ol/sphere/normal.js @@ -0,0 +1,10 @@ +goog.provide('ol.sphere.NORMAL'); + +goog.require('ol.Sphere'); + + +/** + * The normal sphere. + * @const {ol.Sphere} + */ +ol.sphere.NORMAL = new ol.Sphere(6370997); From d3e49d0a19368fdf7214006ac0158ef18b03c6fb Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 3 Mar 2013 17:15:56 +0100 Subject: [PATCH 3/6] Add ol.Projection.getPointResolution --- src/ol/projection.exports | 1 + src/ol/projection.js | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/ol/projection.exports b/src/ol/projection.exports index 613ac99a3c..e82e78498f 100644 --- a/src/ol/projection.exports +++ b/src/ol/projection.exports @@ -2,6 +2,7 @@ @exportProperty ol.Projection.prototype.getAxisOrientation @exportProperty ol.Projection.prototype.getCode @exportProperty ol.Projection.prototype.getExtent +@exportProperty ol.Projection.prototype.getPointResolution @exportProperty ol.Projection.prototype.getUnits @exportSymbol ol.ProjectionUnits diff --git a/src/ol/projection.js b/src/ol/projection.js index 30cc5deb2c..f55f4ba45e 100644 --- a/src/ol/projection.js +++ b/src/ol/projection.js @@ -85,6 +85,14 @@ ol.Projection.prototype.getExtent = function() { }; +/** + * @param {number} resolution Resolution. + * @param {ol.Coordinate} point Point. + * @return {number} Point resolution. + */ +ol.Projection.prototype.getPointResolution = goog.abstractMethod; + + /** * @return {ol.ProjectionUnits} Units. */ From 98d7391024cc032f15f5f2e4e3b85ab51c4a54ff Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 3 Mar 2013 17:17:13 +0100 Subject: [PATCH 4/6] Implement getPointResolution for EPSG:3587 --- src/ol/projection/epsg3857.js | 10 +++++ test/spec/ol/projection.epsg3857.test.js | 49 ++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 test/spec/ol/projection.epsg3857.test.js diff --git a/src/ol/projection/epsg3857.js b/src/ol/projection/epsg3857.js index 0d183c3e66..93a99f8a62 100644 --- a/src/ol/projection/epsg3857.js +++ b/src/ol/projection/epsg3857.js @@ -4,6 +4,7 @@ goog.require('goog.array'); goog.require('ol.Extent'); goog.require('ol.Projection'); goog.require('ol.ProjectionUnits'); +goog.require('ol.math'); goog.require('ol.projection'); @@ -128,3 +129,12 @@ ol.projection.EPSG3857.toEPSG4326 = function(input, opt_output, opt_dimension) { } return output; }; + + +/** + * @inheritDoc + */ +ol.projection.EPSG3857.prototype.getPointResolution = + function(resolution, point) { + return resolution / ol.math.cosh(point.y / ol.projection.EPSG3857.RADIUS); +}; diff --git a/test/spec/ol/projection.epsg3857.test.js b/test/spec/ol/projection.epsg3857.test.js new file mode 100644 index 0000000000..51f72444ec --- /dev/null +++ b/test/spec/ol/projection.epsg3857.test.js @@ -0,0 +1,49 @@ +goog.provide('ol.test.projection.EPSG3857'); + + +describe('ol.projection.EPSG3857', function() { + + describe('getPointResolution', function() { + + it('returns the correct point scale at the equator', function() { + // @see http://msdn.microsoft.com/en-us/library/aa940990.aspx + var epsg3857 = ol.projection.getFromCode('EPSG:3857'); + var resolution = 19.11; + var point = new ol.Coordinate(0, 0); + expect(epsg3857.getPointResolution(resolution, point)). + toRoughlyEqual(19.11, 1e-1); + }); + + it('returns the correct point scale at the latitude of Toronto', + function() { + // @see http://msdn.microsoft.com/en-us/library/aa940990.aspx + var epsg3857 = ol.projection.getFromCode('EPSG:3857'); + var epsg4326 = ol.projection.getFromCode('EPSG:4326'); + var resolution = 19.11; + var point = ol.projection.transform( + new ol.Coordinate(0, 43.65), epsg4326, epsg3857); + expect(epsg3857.getPointResolution(resolution, point)). + toRoughlyEqual(19.11 * Math.cos(Math.PI * 43.65 / 180), 1e-9); + }); + + it('returns the correct point scale at various latitudes', function() { + // @see http://msdn.microsoft.com/en-us/library/aa940990.aspx + var epsg3857 = ol.projection.getFromCode('EPSG:3857'); + var epsg4326 = ol.projection.getFromCode('EPSG:4326'); + var resolution = 19.11; + var latitude; + for (latitude = 0; latitude < 90; ++latitude) { + var point = ol.projection.transform( + new ol.Coordinate(0, latitude), epsg4326, epsg3857); + expect(epsg3857.getPointResolution(resolution, point)). + toRoughlyEqual(19.11 * Math.cos(Math.PI * latitude / 180), 1e-9); + } + }); + + }); + +}); + + +goog.require('ol.Coordinate'); +goog.require('ol.projection.EPSG3857'); From 343768df6ac2108e8a3ca1a8ad854317c4059132 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 3 Mar 2013 17:17:33 +0100 Subject: [PATCH 5/6] Implement getPointResolution for EPSG:4326 --- src/ol/projection/epsg4326.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ol/projection/epsg4326.js b/src/ol/projection/epsg4326.js index 05b2c760d7..1c5d7215aa 100644 --- a/src/ol/projection/epsg4326.js +++ b/src/ol/projection/epsg4326.js @@ -41,3 +41,12 @@ ol.projection.EPSG4326.PROJECTIONS = [ new ol.projection.EPSG4326('urn:ogc:def:crs:EPSG:6.6:4326', 'neu'), new ol.projection.EPSG4326('urn:ogc:def:crs:OGC:1.3:CRS84') ]; + + +/** + * @inheritDoc + */ +ol.projection.EPSG4326.prototype.getPointResolution = + function(resolution, point) { + return resolution; +}; From a8760108d4e519190d344f83537342d5c1f00f30 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 3 Mar 2013 17:19:18 +0100 Subject: [PATCH 6/6] Estimate point resolution for Proj4js projections --- src/ol/projection.js | 40 +++++++++++++++++++++++++++++++++ test/spec/ol/projection.test.js | 30 +++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/src/ol/projection.js b/src/ol/projection.js index f55f4ba45e..0969b45882 100644 --- a/src/ol/projection.js +++ b/src/ol/projection.js @@ -8,6 +8,7 @@ goog.require('goog.object'); goog.require('ol.Coordinate'); goog.require('ol.Extent'); goog.require('ol.TransformFunction'); +goog.require('ol.sphere.NORMAL'); /** @@ -129,10 +130,49 @@ ol.Proj4jsProjection_ = function(code, proj4jsProj) { */ this.proj4jsProj_ = proj4jsProj; + /** + * @private + * @type {?ol.TransformFunction} + */ + this.toEPSG4326_ = null; + }; goog.inherits(ol.Proj4jsProjection_, ol.Projection); +/** + * @inheritDoc + */ +ol.Proj4jsProjection_.prototype.getPointResolution = + function(resolution, point) { + if (this.getUnits() == ol.ProjectionUnits.DEGREES) { + return resolution; + } else { + // Estimate point resolution by transforming the center pixel to EPSG:4326, + // measuring its width and height on the normal sphere, and taking the + // average of the width and height. + if (goog.isNull(this.toEPSG4326_)) { + this.toEPSG4326_ = ol.projection.getTransform( + this, ol.projection.getProj4jsProjectionFromCode_('EPSG:4326')); + } + var vertices = [ + point.x - resolution / 2, point.y, + point.x + resolution / 2, point.y, + point.x, point.y - resolution / 2, + point.x, point.y + resolution / 2 + ]; + vertices = this.toEPSG4326_(vertices, vertices, 2); + var width = ol.sphere.NORMAL.haversineDistance( + new ol.Coordinate(vertices[0], vertices[1]), + new ol.Coordinate(vertices[2], vertices[3])); + var height = ol.sphere.NORMAL.haversineDistance( + new ol.Coordinate(vertices[4], vertices[5]), + new ol.Coordinate(vertices[6], vertices[7])); + return (width + height) / 2; + } +}; + + /** * @return {Proj4js.Proj} Proj4js projection. */ diff --git a/test/spec/ol/projection.test.js b/test/spec/ol/projection.test.js index 5a217e0f40..c961d41819 100644 --- a/test/spec/ol/projection.test.js +++ b/test/spec/ol/projection.test.js @@ -144,6 +144,36 @@ describe('ol.projection', function() { expect(point.y).toRoughlyEqual(200146.976, 1); }); + it('numerically estimates point scale at the equator', function() { + var googleProjection = ol.projection.getFromCode('GOOGLE'); + expect(googleProjection.getPointResolution(1, new ol.Coordinate(0, 0))). + toRoughlyEqual(1, 1e-1); + }); + + it('numerically estimates point scale at various latitudes', function() { + var epsg3857Projection = ol.projection.getFromCode('EPSG:3857'); + var googleProjection = ol.projection.getFromCode('GOOGLE'); + var point, y; + for (y = -20; y <= 20; ++y) { + point = new ol.Coordinate(0, 1000000 * y); + expect(googleProjection.getPointResolution(1, point)).toRoughlyEqual( + epsg3857Projection.getPointResolution(1, point), 1e-1); + } + }); + + it('numerically estimates point scale at various points', function() { + var epsg3857Projection = ol.projection.getFromCode('EPSG:3857'); + var googleProjection = ol.projection.getFromCode('GOOGLE'); + var point, x, y; + for (x = -20; x <= 20; ++x) { + for (y = -20; y <= 20; ++y) { + point = new ol.Coordinate(1000000 * x, 1000000 * y); + expect(googleProjection.getPointResolution(1, point)).toRoughlyEqual( + epsg3857Projection.getPointResolution(1, point), 1e-1); + } + } + }); + }); describe('ol.projection.getTransform()', function() {