diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md index 68da74090e..83656c80bc 100644 --- a/changelog/upgrade-notes.md +++ b/changelog/upgrade-notes.md @@ -1,5 +1,28 @@ ## Upgrade notes +### v6.15.0 + +#### Fixed coordinate dimesion handling in `ol/proj`'s `addCoordinateTransforms` + +The `forward` and `inverse` functions passed to `addCooordinateTransforms` now receive a coordinate with all dimensions of the original coordinate, not just two. If you previosly had coordinates with more than two dimensions and added a transform like +```js +addCoordinateTransforms( + 'EPSG:4326', + new Projection({code: 'latlong', units: 'degrees}), + function(coordinate) { return coordinate.reverse(); }, + function(coordinate) { return coordinate.reverse(); } +); +``` +you have to change that to +```js +addCoordinateTransforms( + 'EPSG:4326', + new Projection({code: 'latlong', units: 'degrees}), + function(coordinate) { return coordinate.slice(0, 2).reverse() }, + function(coordinate) { return coordinate.slice(0, 2).reverse() } +); +``` + ### v6.14.0 No special changes are required when upgrading to the 6.14.0 release. diff --git a/src/ol/proj.js b/src/ol/proj.js index cc0f0d5883..9cceb41fdc 100644 --- a/src/ol/proj.js +++ b/src/ol/proj.js @@ -340,11 +340,10 @@ export function createTransformFromCoordinateTransform(coordTransform) { const dimension = opt_dimension !== undefined ? opt_dimension : 2; const output = opt_output !== undefined ? opt_output : new Array(length); for (let i = 0; i < length; i += dimension) { - const point = coordTransform([input[i], input[i + 1]]); - output[i] = point[0]; - output[i + 1] = point[1]; - for (let j = dimension - 1; j >= 2; --j) { - output[i + j] = input[i + j]; + const point = coordTransform(input.slice(i, i + dimension)); + const pointLength = point.length; + for (let j = 0, jj = dimension; j < jj; ++j) { + output[i + j] = j >= pointLength ? input[i + j] : point[j]; } } return output; @@ -368,7 +367,10 @@ export function createTransformFromCoordinateTransform(coordTransform) { * @param {function(import("./coordinate.js").Coordinate): import("./coordinate.js").Coordinate} inverse The inverse transform * function (that is, from the destination projection to the source * projection) that takes a {@link module:ol/coordinate~Coordinate} as argument and returns - * the transformed {@link module:ol/coordinate~Coordinate}. + * the transformed {@link module:ol/coordinate~Coordinate}. If the transform function can only + * transform less dimensions than the input coordinate, it is supposeed to return a coordinate + * with only the length it can transform. The other dimensions will be taken unchanged from the + * source. * @api */ export function addCoordinateTransforms(source, destination, forward, inverse) { @@ -706,20 +708,19 @@ export function fromUserResolution(resolution, destProjection) { */ export function createSafeCoordinateTransform(sourceProj, destProj, transform) { return function (coord) { - let sourceX = coord[0]; - let sourceY = coord[1]; let transformed, worldsAway; if (sourceProj.canWrapX()) { const sourceExtent = sourceProj.getExtent(); const sourceExtentWidth = getWidth(sourceExtent); + coord = coord.slice(0); worldsAway = getWorldsAway(coord, sourceProj, sourceExtentWidth); if (worldsAway) { // Move x to the real world - sourceX = sourceX - worldsAway * sourceExtentWidth; + coord[0] = coord[0] - worldsAway * sourceExtentWidth; } - sourceX = clamp(sourceX, sourceExtent[0], sourceExtent[2]); - sourceY = clamp(sourceY, sourceExtent[1], sourceExtent[3]); - transformed = transform([sourceX, sourceY]); + coord[0] = clamp(coord[0], sourceExtent[0], sourceExtent[2]); + coord[1] = clamp(coord[1], sourceExtent[1], sourceExtent[3]); + transformed = transform(coord); } else { transformed = transform(coord); } diff --git a/test/node/ol/proj.test.js b/test/node/ol/proj.test.js index 9958022f85..6c85776de7 100644 --- a/test/node/ol/proj.test.js +++ b/test/node/ol/proj.test.js @@ -7,6 +7,7 @@ import {HALF_SIZE} from '../../../src/ol/proj/epsg3857.js'; import { METERS_PER_UNIT, addCommon, + addCoordinateTransforms, clearAllProjections, clearUserProjection, disableCoordinateWarning, @@ -776,6 +777,26 @@ describe('ol/proj.js', function () { expect(got[2]).to.be(3); }); + it('transforms a 3d coordinate with 2-dimension transform', function () { + const latlon = new Projection({ + code: 'latlon', + }); + addCoordinateTransforms( + 'EPSG:4326', + latlon, + function (coordinate) { + return coordinate.slice(0, 2).reverse(); + }, + function (coordinate) { + return coordinate.slice(0, 2).reverse(); + } + ); + + const got = transform([-10, -20, 3], 'EPSG:4326', latlon); + expect(got).to.have.length(3); + expect(got).to.eql([-20, -10, 3]); + }); + it('transforms a 4d coordinate', function () { const got = transform([-10, -20, 3, 4], 'EPSG:4326', 'EPSG:3857'); expect(got).to.have.length(4); @@ -805,6 +826,47 @@ describe('ol/proj.js', function () { addCommon(); }); + it('works with 3d points and proj4 defs for 3d transforms', function () { + proj4.defs( + 'geocent', + '+proj=geocent +datum=WGS84 +ellps=WGS84 +units=m +no_defs' + ); + register(proj4); + + const got = transform( + [5584000, 2844000, 3448000], + 'geocent', + 'EPSG:4326' + ); + expect(got).to.have.length(3); + expect(got[0]).to.roughlyEqual(26.990304649234826, 1e-9); + expect(got[1]).to.roughlyEqual(28.965718227798618, 1e-9); + expect(got[2]).to.roughlyEqual(779337.8584198505, 1e-9); + + delete proj4.defs.geocent; + clearAllProjections(); + addCommon(); + }); + + it('works with 3d points and proj4 defs for 3d transforms with clamped extent', function () { + proj4.defs( + 'geocent', + '+proj=geocent +datum=WGS84 +ellps=WGS84 +units=m +no_defs' + ); + register(proj4); + + const got = transform([-7.56234, 38.96618, 0], 'EPSG:4326', 'geocent'); + + expect(got).to.have.length(3); + expect(got[0]).to.roughlyEqual(4922499, 1); + expect(got[1]).to.roughlyEqual(-653508, 1); + expect(got[2]).to.roughlyEqual(3989398, 1); + + delete proj4.defs.geocent; + clearAllProjections(); + addCommon(); + }); + it('does not flip axis order', function () { proj4.defs('enu', '+proj=longlat'); proj4.defs('neu', '+proj=longlat +axis=neu'); @@ -812,7 +874,6 @@ describe('ol/proj.js', function () { const got = transform([1, 2], 'neu', 'enu'); expect(got).to.eql([1, 2]); - delete proj4.defs.enu; delete proj4.defs.neu; clearAllProjections();