diff --git a/src/ol/proj.js b/src/ol/proj.js index 763d0210b0..cd0fefea0e 100644 --- a/src/ol/proj.js +++ b/src/ol/proj.js @@ -498,6 +498,105 @@ export function transformWithProjections(point, sourceProjection, destinationPro return transformFunc(point); } +/** + * @type {Projection} + */ +let userProjection = null; + +/** + * Set the projection for coordinates supplied from and returned by API methods. + * Note that this method is not yet a part of the stable API. Support for user + * projections is not yet complete and should be considered experimental. + * @param {ProjectionLike} projection The user projection. + */ +export function setUserProjection(projection) { + userProjection = get(projection); +} + +/** + * Clear the user projection if set. Note that this method is not yet a part of + * the stable API. Support for user projections is not yet complete and should + * be considered experimental. + */ +export function clearUserProjection() { + userProjection = null; +} + +/** + * Get the projection for coordinates supplied from and returned by API methods. + * Note that this method is not yet a part of the stable API. Support for user + * projections is not yet complete and should be considered experimental. + * @returns {Projection} The user projection (or null if not set). + */ +export function getUserProjection() { + return userProjection; +} + +/** + * Use geographic coordinates (WGS-84 datum) in API methods. Note that this + * method is not yet a part of the stable API. Support for user projections is + * not yet complete and should be considered experimental. + */ +export function useGeographic() { + setUserProjection('EPSG:4326'); +} + +/** + * Return a coordinate transformed into the user projection. If no user projection + * is set, the original coordinate is returned. + * @param {Array} coordinate Input coordinate. + * @param {ProjectionLike} sourceProjection The input coordinate projection. + * @returns {Array} The input coordinate in the user projection. + */ +export function toUserCoordinate(coordinate, sourceProjection) { + if (!userProjection) { + return coordinate; + } + return transform(coordinate, sourceProjection, userProjection); +} + +/** + * Return a coordinate transformed from the user projection. If no user projection + * is set, the original coordinate is returned. + * @param {Array} coordinate Input coordinate. + * @param {ProjectionLike} destProjection The destination projection. + * @returns {Array} The input coordinate transformed. + */ +export function fromUserCoordinate(coordinate, destProjection) { + if (!userProjection) { + return coordinate; + } + return transform(coordinate, userProjection, destProjection); +} + +/** + * Return an extent transformed into the user projection. If no user projection + * is set, the original extent is returned. + * @param {import("./extent.js").Extent} extent Input extent. + * @param {ProjectionLike} sourceProjection The input extent projection. + * @returns {import("./extent.js").Extent} The input extent in the user projection. + */ +export function toUserExtent(extent, sourceProjection) { + if (!userProjection) { + return extent; + } + return transformExtent(extent, sourceProjection, userProjection); +} + +/** + * Return an extent transformed from the user projection. If no user projection + * is set, the original extent is returned. + * @param {import("./extent.js").Extent} extent Input extent. + * @param {ProjectionLike} destProjection The destination projection. + * @returns {import("./extent.js").Extent} The input extent transformed. + */ +export function fromUserExtent(extent, destProjection) { + if (!userProjection) { + return extent; + } + return transformExtent(extent, userProjection, destProjection); +} + /** * Add transforms to and from EPSG:4326 and EPSG:3857. This function is called * by when this module is executed and should only need to be called again after diff --git a/test/spec/ol/proj.test.js b/test/spec/ol/proj.test.js index 797a5a66d4..986767ad2d 100644 --- a/test/spec/ol/proj.test.js +++ b/test/spec/ol/proj.test.js @@ -1,4 +1,12 @@ import { + useGeographic, + getUserProjection, + setUserProjection, + clearUserProjection, + toUserCoordinate, + fromUserCoordinate, + toUserExtent, + fromUserExtent, addCommon, clearAllProjections, equivalent, @@ -22,9 +30,114 @@ describe('ol.proj', function() { afterEach(function() { clearAllProjections(); + clearUserProjection(); addCommon(); }); + describe('useGeographic()', function() { + it('sets the user projection to Geographic/WGS-84', function() { + useGeographic(); + const projection = getUserProjection(); + expect(projection).to.be(getProjection('EPSG:4326')); + }); + }); + + describe('getUserProjection()', function() { + it('returns null by default', function() { + expect(getUserProjection()).to.be(null); + }); + + it('returns the user projection if set', function() { + const projection = getProjection('EPSG:4326'); + setUserProjection(projection); + expect(getUserProjection()).to.be(projection); + }); + }); + + describe('setUserProjection()', function() { + it('accepts a string identifier', function() { + const projection = getProjection('EPSG:4326'); + setUserProjection('EPSG:4326'); + expect(getUserProjection()).to.be(projection); + }); + }); + + describe('clearUserProjection()', function() { + it('clears the user projection', function() { + useGeographic(); + clearUserProjection(); + expect(getUserProjection()).to.be(null); + }); + }); + + describe('toUserCoordinate()', function() { + it('transforms a point to the user projection', function() { + useGeographic(); + const coordinate = fromLonLat([-110, 45]); + const user = toUserCoordinate(coordinate, 'EPSG:3857'); + const transformed = transform(coordinate, 'EPSG:3857', 'EPSG:4326'); + expect(user).to.eql(transformed); + expect(user).not.to.eql(coordinate); + }); + + it('returns the original if no user projection is set', function() { + const coordinate = fromLonLat([-110, 45]); + const user = toUserCoordinate(coordinate, 'EPSG:3857'); + expect(user).to.be(coordinate); + }); + }); + + describe('fromUserCoordinate()', function() { + it('transforms a point from the user projection', function() { + useGeographic(); + const user = [-110, 45]; + const coordinate = fromUserCoordinate(user, 'EPSG:3857'); + const transformed = transform(user, 'EPSG:4326', 'EPSG:3857'); + expect(coordinate).to.eql(transformed); + expect(user).not.to.eql(coordinate); + }); + + it('returns the original if no user projection is set', function() { + const user = fromLonLat([-110, 45]); + const coordinate = fromUserCoordinate(user, 'EPSG:3857'); + expect(coordinate).to.be(user); + }); + }); + + describe('toUserExtent()', function() { + it('transforms an extent to the user projection', function() { + useGeographic(); + const extent = transformExtent([-110, 45, -100, 50], 'EPSG:4326', 'EPSG:3857'); + const user = toUserExtent(extent, 'EPSG:3857'); + const transformed = transformExtent(extent, 'EPSG:3857', 'EPSG:4326'); + expect(user).to.eql(transformed); + expect(user).not.to.eql(extent); + }); + + it('returns the original if no user projection is set', function() { + const extent = transformExtent([-110, 45, -100, 50], 'EPSG:4326', 'EPSG:3857'); + const user = toUserExtent(extent, 'EPSG:3857'); + expect(user).to.be(extent); + }); + }); + + describe('fromUserExtent()', function() { + it('transforms an extent from the user projection', function() { + useGeographic(); + const user = [-110, 45, -100, 50]; + const extent = fromUserExtent(user, 'EPSG:3857'); + const transformed = transformExtent(user, 'EPSG:4326', 'EPSG:3857'); + expect(extent).to.eql(transformed); + expect(extent).not.to.eql(user); + }); + + it('returns the original if no user projection is set', function() { + const user = transformExtent([-110, 45, -100, 50], 'EPSG:4326', 'EPSG:3857'); + const extent = fromUserExtent(user, 'EPSG:3857'); + expect(extent).to.be(user); + }); + }); + describe('toLonLat()', function() { const cases = [{ from: [0, 0],