Make proj4 transforms behave like built-in transforms
This commit is contained in:
@@ -410,17 +410,22 @@ export function toStringXY(coordinate, opt_fractionDigits) {
|
|||||||
* @return {Coordinate} The coordinate within the real world extent.
|
* @return {Coordinate} The coordinate within the real world extent.
|
||||||
*/
|
*/
|
||||||
export function wrapX(coordinate, projection) {
|
export function wrapX(coordinate, projection) {
|
||||||
const projectionExtent = projection.getExtent();
|
const worldsAway = getWorldsAway(coordinate, projection);
|
||||||
if (
|
if (worldsAway) {
|
||||||
projection.canWrapX() &&
|
coordinate[0] -= worldsAway * getWidth(projection.getExtent());
|
||||||
(coordinate[0] < projectionExtent[0] ||
|
|
||||||
coordinate[0] >= projectionExtent[2])
|
|
||||||
) {
|
|
||||||
const worldWidth = getWidth(projectionExtent);
|
|
||||||
const worldsAway = Math.floor(
|
|
||||||
(coordinate[0] - projectionExtent[0]) / worldWidth
|
|
||||||
);
|
|
||||||
coordinate[0] -= worldsAway * worldWidth;
|
|
||||||
}
|
}
|
||||||
return coordinate;
|
return coordinate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getWorldsAway(coordinate, projection) {
|
||||||
|
const projectionExtent = projection.getExtent();
|
||||||
|
let worldsAway = 0;
|
||||||
|
if (
|
||||||
|
projection.canWrapX() &&
|
||||||
|
(coordinate[0] < projectionExtent[0] || coordinate[0] > projectionExtent[2])
|
||||||
|
) {
|
||||||
|
const worldWidth = getWidth(projectionExtent);
|
||||||
|
worldsAway = Math.floor((coordinate[0] - projectionExtent[0]) / worldWidth);
|
||||||
|
}
|
||||||
|
return worldsAway;
|
||||||
|
}
|
||||||
|
|||||||
@@ -71,9 +71,10 @@ import {
|
|||||||
clear as clearTransformFuncs,
|
clear as clearTransformFuncs,
|
||||||
get as getTransformFunc,
|
get as getTransformFunc,
|
||||||
} from './proj/transforms.js';
|
} from './proj/transforms.js';
|
||||||
import {applyTransform} from './extent.js';
|
import {applyTransform, getWidth} from './extent.js';
|
||||||
|
import {clamp, modulo} from './math.js';
|
||||||
import {getDistance} from './sphere.js';
|
import {getDistance} from './sphere.js';
|
||||||
import {modulo} from './math.js';
|
import {getWorldsAway} from './coordinate.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A projection as {@link module:ol/proj/Projection}, SRS identifier
|
* A projection as {@link module:ol/proj/Projection}, SRS identifier
|
||||||
@@ -625,6 +626,55 @@ export function fromUserExtent(extent, destProjection) {
|
|||||||
return transformExtent(extent, userProjection, destProjection);
|
return transformExtent(extent, userProjection, destProjection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a safe coordinate transform function from a coordinate transform function.
|
||||||
|
* "Safe" means that it can handle wrapping of x-coordinates for global projections,
|
||||||
|
* and that coordinates exceeding the projection validity extent's range will be
|
||||||
|
* clamped to the validity range.
|
||||||
|
* @param {Projection} sourceProj Source projection.
|
||||||
|
* @param {Projection} destProj Destination projection.
|
||||||
|
* @param {function(import("./coordinate.js").Coordinate): import("./coordinate.js").Coordinate} forward Transform function (source to destiation).
|
||||||
|
* @param {function(import("./coordinate.js").Coordinate): import("./coordinate.js").Coordinate} inverse Transform function (destiation to source).
|
||||||
|
* @return {function(import("./coordinate.js").Coordinate): import("./coordinate.js").Coordinate} Safe transform function (source to destiation).
|
||||||
|
*/
|
||||||
|
export function createSafeCoordinateTransform(
|
||||||
|
sourceProj,
|
||||||
|
destProj,
|
||||||
|
forward,
|
||||||
|
inverse
|
||||||
|
) {
|
||||||
|
return function (coord) {
|
||||||
|
const worldsAway = getWorldsAway(coord, sourceProj);
|
||||||
|
const sourceExtent = sourceProj.getExtent();
|
||||||
|
const destExtent = destProj.getExtent();
|
||||||
|
let clampBottom, clampTop;
|
||||||
|
if (destExtent) {
|
||||||
|
const clampBottomLeft = inverse(destExtent.slice(0, 2));
|
||||||
|
const clampTopRight = inverse(destExtent.slice(2, 4));
|
||||||
|
clampBottom = isNaN(clampBottomLeft[1])
|
||||||
|
? sourceExtent
|
||||||
|
? sourceExtent[1]
|
||||||
|
: undefined
|
||||||
|
: clampBottomLeft[1];
|
||||||
|
clampTop = isNaN(clampTopRight[1])
|
||||||
|
? sourceExtent
|
||||||
|
? sourceExtent[3]
|
||||||
|
: undefined
|
||||||
|
: clampTopRight[1];
|
||||||
|
}
|
||||||
|
const transformed = sourceExtent
|
||||||
|
? forward([
|
||||||
|
coord[0] - worldsAway * getWidth(sourceExtent),
|
||||||
|
clampTop ? clamp(coord[1], clampBottom, clampTop) : coord[1],
|
||||||
|
])
|
||||||
|
: forward(coord);
|
||||||
|
if (worldsAway && destProj.canWrapX()) {
|
||||||
|
transformed[0] += worldsAway * getWidth(destProj.getExtent());
|
||||||
|
}
|
||||||
|
return transformed;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add transforms to and from EPSG:4326 and EPSG:3857. This function is called
|
* 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
|
* by when this module is executed and should only need to be called again after
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
addCoordinateTransforms,
|
addCoordinateTransforms,
|
||||||
addEquivalentProjections,
|
addEquivalentProjections,
|
||||||
addProjection,
|
addProjection,
|
||||||
|
createSafeCoordinateTransform,
|
||||||
get,
|
get,
|
||||||
} from '../proj.js';
|
} from '../proj.js';
|
||||||
import {assign} from '../obj.js';
|
import {assign} from '../obj.js';
|
||||||
@@ -60,8 +61,18 @@ export function register(proj4) {
|
|||||||
addCoordinateTransforms(
|
addCoordinateTransforms(
|
||||||
proj1,
|
proj1,
|
||||||
proj2,
|
proj2,
|
||||||
transform.forward,
|
createSafeCoordinateTransform(
|
||||||
transform.inverse
|
proj1,
|
||||||
|
proj2,
|
||||||
|
transform.forward,
|
||||||
|
transform.inverse
|
||||||
|
),
|
||||||
|
createSafeCoordinateTransform(
|
||||||
|
proj2,
|
||||||
|
proj1,
|
||||||
|
transform.inverse,
|
||||||
|
transform.forward
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -560,6 +560,25 @@ describe('ol.proj', function () {
|
|||||||
});
|
});
|
||||||
expect(getProjection('EPSG:4326')).to.equal(epsg4326);
|
expect(getProjection('EPSG:4326')).to.equal(epsg4326);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses safe transform functions', function () {
|
||||||
|
register(proj4);
|
||||||
|
const wgs84 = getProjection('WGS84');
|
||||||
|
const epsg4326 = getProjection('EPSG:4326');
|
||||||
|
wgs84.setExtent(epsg4326.getExtent());
|
||||||
|
wgs84.setGlobal(true);
|
||||||
|
const google = getProjection('GOOGLE');
|
||||||
|
const epsg3857 = getProjection('EPSG:3857');
|
||||||
|
google.setExtent(epsg3857.getExtent());
|
||||||
|
google.setGlobal(true);
|
||||||
|
const coord = [-190, 90];
|
||||||
|
const transformed = transform(coord, wgs84, google);
|
||||||
|
expect(transformed).to.eql(transform(coord, epsg4326, epsg3857));
|
||||||
|
const got = transform(transformed, google, wgs84);
|
||||||
|
const expected = transform(transformed, epsg3857, epsg4326);
|
||||||
|
expect(got[0]).to.roughlyEqual(expected[0], 1e-9);
|
||||||
|
expect(got[1]).to.roughlyEqual(expected[1], 1e-9);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ol.proj.getTransformFromProjections()', function () {
|
describe('ol.proj.getTransformFromProjections()', function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user