From a9b3a5bcc9747283dfca68e0758b592cceac1ba9 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 6 Jul 2020 22:35:34 +0200 Subject: [PATCH] Only clamp to source projection extent --- .../reproj-azimuthal-equal-area/expected.png | Bin 0 -> 4084 bytes .../cases/reproj-azimuthal-equal-area/main.js | 57 ++++++++++++++++++ src/ol/proj.js | 56 +++++------------ src/ol/proj/proj4.js | 14 +---- test/spec/ol/proj.test.js | 19 +++--- 5 files changed, 85 insertions(+), 61 deletions(-) create mode 100644 rendering/cases/reproj-azimuthal-equal-area/expected.png create mode 100644 rendering/cases/reproj-azimuthal-equal-area/main.js diff --git a/rendering/cases/reproj-azimuthal-equal-area/expected.png b/rendering/cases/reproj-azimuthal-equal-area/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..c41abcff6e12442be8309dc60431eac1de2337ec GIT binary patch literal 4084 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 F{{>HFoUH%= literal 0 HcmV?d00001 diff --git a/rendering/cases/reproj-azimuthal-equal-area/main.js b/rendering/cases/reproj-azimuthal-equal-area/main.js new file mode 100644 index 0000000000..1dd05f2452 --- /dev/null +++ b/rendering/cases/reproj-azimuthal-equal-area/main.js @@ -0,0 +1,57 @@ +import ImageCanvas from '../../../src/ol/source/ImageCanvas.js'; +import ImageLayer from '../../../src/ol/layer/Image.js'; +import Map from '../../../src/ol/Map.js'; +import View from '../../../src/ol/View.js'; +import proj4 from 'proj4'; +import {get as getProjection, transform} from '../../../src/ol/proj.js'; +import {register} from '../../../src/ol/proj/proj4.js'; + +const llpos = [-72, 40]; + +proj4.defs( + 'az', + '+proj=aeqd +lat_0=' + + llpos[1] + + ' +lon_0=' + + llpos[0] + + ' +x_0=0 +y_0=0 +a=6371000 +b=6371000 +units=m' +); +register(proj4); +const aeqd = getProjection('az'); + +function canvasFunction(extent, resolution, pixelRatio, size) { + size = [Math.round(size[0]), Math.round(size[1])]; + const canvas = document.createElement('canvas'); + canvas.width = size[0]; + canvas.height = size[1]; + + // fill the canvas with blue + const ctx = canvas.getContext('2d'); + ctx.fillStyle = 'blue'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + return canvas; +} + +const canvasLayer = new ImageLayer({ + source: new ImageCanvas({ + projection: 'EPSG:4326', + canvasFunction: canvasFunction, + }), +}); + +const center = transform(llpos, 'EPSG:4326', aeqd); + +new Map({ + pixelRatio: 1, + target: 'map', + layers: [canvasLayer], + view: new View({ + center: center, + projection: aeqd, + zoom: 0, + }), +}); + +render({ + tolerance: 0.001, +}); diff --git a/src/ol/proj.js b/src/ol/proj.js index 347d4fa017..9d59af3dc0 100644 --- a/src/ol/proj.js +++ b/src/ol/proj.js @@ -71,14 +71,7 @@ import { clear as clearTransformFuncs, get as getTransformFunc, } from './proj/transforms.js'; -import { - applyTransform, - getBottomLeft, - getBottomRight, - getTopLeft, - getTopRight, - getWidth, -} from './extent.js'; +import {applyTransform, getWidth} from './extent.js'; import {clamp, modulo} from './math.js'; import {getDistance} from './sphere.js'; import {getWorldsAway} from './coordinate.js'; @@ -636,56 +629,35 @@ export function fromUserExtent(extent, 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 + * and that coordinates exceeding the source 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). + * @param {function(import("./coordinate.js").Coordinate): import("./coordinate.js").Coordinate} transform Transform function (source to destiation). * @return {function(import("./coordinate.js").Coordinate): import("./coordinate.js").Coordinate} Safe transform function (source to destiation). */ -export function createSafeCoordinateTransform( - sourceProj, - destProj, - forward, - inverse -) { +export function createSafeCoordinateTransform(sourceProj, destProj, transform) { return function (coord) { - let x, y, worldsAway; + let sourceX = coord[0]; + let sourceY = coord[1]; + let transformed, worldsAway; if (sourceProj.canWrapX()) { const sourceExtent = sourceProj.getExtent(); const sourceExtentWidth = getWidth(sourceExtent); worldsAway = getWorldsAway(coord, sourceProj, sourceExtentWidth); if (worldsAway) { // Move x to the real world - x = coord[0] - worldsAway * sourceExtentWidth; - } - } - let transformed = forward(x === undefined ? coord : [x, coord[1]]); - const destExtent = destProj.getExtent(); - if (!isFinite(transformed[0]) || !isFinite(transformed[1])) { - // Try to recover from out-of-bounds transform - if (destExtent) { - const corner1 = inverse(getBottomLeft(destExtent)); - const corner2 = inverse(getBottomRight(destExtent)); - const corner3 = inverse(getTopLeft(destExtent)); - const corner4 = inverse(getTopRight(destExtent)); - const minX = Math.min(corner1[0], corner2[0], corner3[0], corner4[0]); - const maxX = Math.max(corner1[0], corner2[0], corner3[0], corner4[0]); - const minY = Math.min(corner1[1], corner2[1], corner3[1], corner4[1]); - const maxY = Math.max(corner1[1], corner2[1], corner3[1], corner4[1]); - if (isFinite(minX) && isFinite(maxX)) { - x = clamp(x == undefined ? coord[1] : x, minX, maxX); - } - if (isFinite(minY) && isFinite(maxY)) { - y = clamp(coord[1], minY, maxY); - } - transformed = forward([x, y]); + sourceX = sourceX - worldsAway * sourceExtentWidth; } + sourceX = clamp(sourceX, sourceExtent[0], sourceExtent[2]); + sourceY = clamp(sourceY, sourceExtent[1], sourceExtent[3]); + transformed = transform([sourceX, sourceY]); + } else { + transformed = transform(coord); } if (worldsAway && destProj.canWrapX()) { // Move transformed coordinate back to the offset world - transformed[0] += worldsAway * getWidth(destExtent); + transformed[0] += worldsAway * getWidth(destProj.getExtent()); } return transformed; }; diff --git a/src/ol/proj/proj4.js b/src/ol/proj/proj4.js index f4fb255074..1726c5d7ce 100644 --- a/src/ol/proj/proj4.js +++ b/src/ol/proj/proj4.js @@ -61,18 +61,8 @@ export function register(proj4) { addCoordinateTransforms( proj1, proj2, - createSafeCoordinateTransform( - proj1, - proj2, - transform.forward, - transform.inverse - ), - createSafeCoordinateTransform( - proj2, - proj1, - transform.inverse, - transform.forward - ) + createSafeCoordinateTransform(proj1, proj2, transform.forward), + createSafeCoordinateTransform(proj2, proj1, transform.inverse) ); } } diff --git a/test/spec/ol/proj.test.js b/test/spec/ol/proj.test.js index bc5990256b..be4cd588e9 100644 --- a/test/spec/ol/proj.test.js +++ b/test/spec/ol/proj.test.js @@ -571,13 +571,18 @@ describe('ol.proj', function () { 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); + + const coord = [-190, 85]; + + let expected = transform(coord, wgs84, google); + let got = transform(coord, epsg4326, epsg3857); + expect(got[0]).to.roughlyEqual(expected[0], 1e-7); + expect(got[1]).to.roughlyEqual(expected[1], 1e-7); + + expected = transform(expected, google, wgs84); + got = transform(got, epsg3857, epsg4326); + expect(got[0]).to.roughlyEqual(expected[0], 1e-7); + expect(got[1]).to.roughlyEqual(expected[1], 1e-7); }); });