From 6ae6d0b835ae41d2d3e0095079ce84d0cff0b297 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Sat, 22 Feb 2020 22:40:29 +0000 Subject: [PATCH 1/8] Optional extra stops to refine extent transforms Add optional extra stops to refine some non-parallel transforms Refine extent transforms use by Gratucule by using 32 points (8 per side) instead of the standard corners only transform Update Graticule class description with current limitations Correct projection extent in Mollweide example and revise opening zoom level correspondingly --- examples/sphere-mollweide.js | 6 +++--- src/ol/extent.js | 38 +++++++++++++++++++++++++++--------- src/ol/layer/Graticule.js | 7 ++++--- src/ol/proj.js | 6 ++++-- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/examples/sphere-mollweide.js b/examples/sphere-mollweide.js index 7540cd095b..83fc6adcc4 100644 --- a/examples/sphere-mollweide.js +++ b/examples/sphere-mollweide.js @@ -17,8 +17,8 @@ register(proj4); // and a world extent. These are required for the Graticule. const sphereMollweideProjection = new Projection({ code: 'ESRI:53009', - extent: [-9009954.605703328, -9009954.605703328, - 9009954.605703328, 9009954.605703328], + extent: [-18019909.21177587, -9009954.605703328, + 18019909.21177587, 9009954.605703328], worldExtent: [-179, -89.99, 179, 89.99] }); @@ -37,6 +37,6 @@ const map = new Map({ view: new View({ center: [0, 0], projection: sphereMollweideProjection, - zoom: 0 + zoom: 1 }) }); diff --git a/src/ol/extent.js b/src/ol/extent.js index cc74b803fb..94eb9cb249 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -778,18 +778,38 @@ export function intersectsSegment(extent, start, end) { * @param {import("./proj.js").TransformFunction} transformFn Transform function. * Called with `[minX, minY, maxX, maxY]` extent coordinates. * @param {Extent=} opt_extent Destination extent. + * @param {number=} opt_stops Number of stops per side used for the transform. + * By default only the corners are used. * @return {Extent} Extent. * @api */ -export function applyTransform(extent, transformFn, opt_extent) { - const coordinates = [ - extent[0], extent[1], - extent[0], extent[3], - extent[2], extent[1], - extent[2], extent[3] - ]; +export function applyTransform(extent, transformFn, opt_extent, opt_stops) { + let coordinates = []; + if (opt_stops > 1) { + const width = extent[2] - extent[0]; + const height = extent[3] - extent[1]; + for (let i = 0; i < opt_stops; ++i) { + coordinates.push( + extent[0] + width * i / opt_stops, extent[1], + extent[2], extent[1] + height * i / opt_stops, + extent[2] - width * i / opt_stops, extent[3], + extent[0], extent[3] - height * i / opt_stops + ); + } + } else { + coordinates = [ + extent[0], extent[1], + extent[2], extent[1], + extent[2], extent[3], + extent[0], extent[3] + ]; + } transformFn(coordinates, coordinates, 2); - const xs = [coordinates[0], coordinates[2], coordinates[4], coordinates[6]]; - const ys = [coordinates[1], coordinates[3], coordinates[5], coordinates[7]]; + const xs = []; + const ys = []; + for (let i = 0, l = coordinates.length; i < l; i += 2) { + xs.push(coordinates[i]); + ys.push(coordinates[i + 1]); + } return _boundingExtentXYs(xs, ys, opt_extent); } diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index f1480dddc9..31e0f5aaee 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -144,7 +144,8 @@ const INTERVALS = [ /** * @classdesc - * Layer that renders a grid for a coordinate system. + * Layer that renders a grid for a coordinate system (currently only EPSG:4326 is supported). + * Note that the view projection must define both extent and worldExtent. * * @fires import("../render/Event.js").RenderEvent * @api @@ -677,7 +678,7 @@ class Graticule extends VectorLayer { Math.min(extent[3], this.maxLatP_) ]; - validExtent = transformExtent(validExtent, this.projection_, 'EPSG:4326'); + validExtent = transformExtent(validExtent, this.projection_, 'EPSG:4326', 8); const maxLat = validExtent[3]; const maxLon = validExtent[2]; const minLat = validExtent[1]; @@ -896,7 +897,7 @@ class Graticule extends VectorLayer { const epsg4326Projection = getProjection('EPSG:4326'); const worldExtent = projection.getWorldExtent(); - const worldExtentP = transformExtent(worldExtent, epsg4326Projection, projection); + const worldExtentP = transformExtent(worldExtent, epsg4326Projection, projection, 8); this.maxLat_ = worldExtent[3]; this.maxLon_ = worldExtent[2]; diff --git a/src/ol/proj.js b/src/ol/proj.js index f64cc05a85..d2be0feb3f 100644 --- a/src/ol/proj.js +++ b/src/ol/proj.js @@ -476,12 +476,14 @@ export function transform(coordinate, source, destination) { * @param {import("./extent.js").Extent} extent The extent to transform. * @param {ProjectionLike} source Source projection-like. * @param {ProjectionLike} destination Destination projection-like. + * @param {number=} opt_stops Number of stops per side used for the transform. + * By default only the corners are used. * @return {import("./extent.js").Extent} The transformed extent. * @api */ -export function transformExtent(extent, source, destination) { +export function transformExtent(extent, source, destination, opt_stops) { const transformFunc = getTransform(source, destination); - return applyTransform(extent, transformFunc); + return applyTransform(extent, transformFunc, undefined, opt_stops); } From 737f3a5066bef26fec7a45fae181e77978f0d168 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Mon, 24 Feb 2020 21:53:53 +0000 Subject: [PATCH 2/8] Handle view projections crossing dateline Use custom toLonLat transform to return wrapped longitudes for extents if view projection crosses the dateline. Enhance the validation for center and extents to avoid proj4 errors. --- src/ol/layer/Graticule.js | 71 +++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 31e0f5aaee..6c17962753 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -12,10 +12,18 @@ import VectorSource from '../source/Vector.js'; import { equivalent as equivalentProjection, get as getProjection, - getTransform, - transformExtent + getTransform } from '../proj.js'; -import {getCenter, getHeight, getWidth, intersects, equals, getIntersection, isEmpty} from '../extent.js'; +import { + applyTransform, + equals, + getCenter, + getHeight, + getIntersection, + getWidth, + intersects, + isEmpty +} from '../extent.js'; import {clamp} from '../math.js'; import Style from '../style/Style.js'; import Feature from '../Feature.js'; @@ -665,24 +673,30 @@ class Graticule extends VectorLayer { return; } - const centerLonLat = this.toLonLatTransform_(center); + const validCenter = [ + clamp(center[0], this.minLonP_, this.maxLonP_), + clamp(center[1], this.minLatP_, this.maxLatP_) + ]; + + const centerLonLat = this.toLonLatTransform_(validCenter); let centerLon = centerLonLat[0]; let centerLat = centerLonLat[1]; const maxLines = this.maxLines_; let cnt, idx, lat, lon; let validExtent = [ - Math.max(extent[0], this.minLonP_), - Math.max(extent[1], this.minLatP_), - Math.min(extent[2], this.maxLonP_), - Math.min(extent[3], this.maxLatP_) + clamp(extent[0], this.minLonP_, this.maxLonP_), + clamp(extent[1], this.minLatP_, this.maxLatP_), + clamp(extent[2], this.minLonP_, this.maxLonP_), + clamp(extent[3], this.minLatP_, this.maxLatP_) ]; - validExtent = transformExtent(validExtent, this.projection_, 'EPSG:4326', 8); - const maxLat = validExtent[3]; - const maxLon = validExtent[2]; - const minLat = validExtent[1]; - const minLon = validExtent[0]; + validExtent = applyTransform(validExtent, this.toLonLatTransform_, undefined, 8); + + const maxLat = Math.min(validExtent[3], this.maxLat_); + const maxLon = Math.min(validExtent[2], this.maxLon_); + const minLat = Math.max(validExtent[1], this.minLat_); + const minLon = Math.max(validExtent[0], this.minLon_); // Create meridians @@ -897,22 +911,43 @@ class Graticule extends VectorLayer { const epsg4326Projection = getProjection('EPSG:4326'); const worldExtent = projection.getWorldExtent(); - const worldExtentP = transformExtent(worldExtent, epsg4326Projection, projection, 8); this.maxLat_ = worldExtent[3]; this.maxLon_ = worldExtent[2]; this.minLat_ = worldExtent[1]; this.minLon_ = worldExtent[0]; + const toLonLatTransform = getTransform(projection, epsg4326Projection); + if (this.minLon_ < this.maxLon_) { + this.toLonLatTransform_ = toLonLatTransform; + } else { + const split = this.minLon_ + this.maxLon_ / 2; + this.maxLon_ += 360; + this.toLonLatTransform_ = function(coordinates, opt_output, opt_dimension) { + const dimension = opt_dimension || 2; + const lonLatCoordinates = toLonLatTransform(coordinates, opt_output, dimension); + for (let i = 0, l = lonLatCoordinates.length; i < l; i += dimension) { + if (lonLatCoordinates[i] < split) { + lonLatCoordinates[i] += 360; + } + } + return lonLatCoordinates; + }; + } + + this.fromLonLatTransform_ = getTransform(epsg4326Projection, projection); + const worldExtentP = applyTransform( + [this.minLon_, this.minLat_, this.maxLon_, this.maxLat_], + this.fromLonLatTransform_, + undefined, + 8 + ); + this.maxLatP_ = worldExtentP[3]; this.maxLonP_ = worldExtentP[2]; this.minLatP_ = worldExtentP[1]; this.minLonP_ = worldExtentP[0]; - this.fromLonLatTransform_ = getTransform(epsg4326Projection, projection); - - this.toLonLatTransform_ = getTransform(projection, epsg4326Projection); - this.projectionCenterLonLat_ = this.toLonLatTransform_(getCenter(projection.getExtent())); this.projection_ = projection; From 9039e2629b167a1268866190affab70a0aadf2fc Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Mon, 24 Feb 2020 21:57:22 +0000 Subject: [PATCH 3/8] fix trailing space --- src/ol/layer/Graticule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 6c17962753..b7002c247b 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -677,7 +677,7 @@ class Graticule extends VectorLayer { clamp(center[0], this.minLonP_, this.maxLonP_), clamp(center[1], this.minLatP_, this.maxLatP_) ]; - + const centerLonLat = this.toLonLatTransform_(validCenter); let centerLon = centerLonLat[0]; let centerLat = centerLonLat[1]; From adcf57ef20d03df255b2d1ec45c469d7cc48878c Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Tue, 25 Feb 2020 13:47:02 +0000 Subject: [PATCH 4/8] Better validation to handle polar projections For some projections (e.g. polar) the maximum or minimum values may be at the center. To improve the display for polar projections take validated center values into account when calculating max and min. --- src/ol/layer/Graticule.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index b7002c247b..42b5c380fa 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -679,8 +679,8 @@ class Graticule extends VectorLayer { ]; const centerLonLat = this.toLonLatTransform_(validCenter); - let centerLon = centerLonLat[0]; - let centerLat = centerLonLat[1]; + let centerLon = clamp(centerLonLat[0], this.minLon_, this.maxLon_); + let centerLat = clamp(centerLonLat[1], this.minLat_, this.maxLat_); const maxLines = this.maxLines_; let cnt, idx, lat, lon; @@ -693,10 +693,10 @@ class Graticule extends VectorLayer { validExtent = applyTransform(validExtent, this.toLonLatTransform_, undefined, 8); - const maxLat = Math.min(validExtent[3], this.maxLat_); - const maxLon = Math.min(validExtent[2], this.maxLon_); - const minLat = Math.max(validExtent[1], this.minLat_); - const minLon = Math.max(validExtent[0], this.minLon_); + const maxLat = clamp(validExtent[3], centerLat, this.maxLat_); + const maxLon = clamp(validExtent[2], centerLon, this.maxLon_); + const minLat = clamp(validExtent[1], this.minLat_, centerLat); + const minLon = clamp(validExtent[0], this.minLon_, centerLon); // Create meridians From 591e5ce01bbf9ccffe9e4d749584abcc1f77c5f3 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Wed, 26 Feb 2020 22:17:49 +0000 Subject: [PATCH 5/8] More validation. Handle voids in some projections. Handle NaN latitude transform results at void centers of some projections (e.g. Pole LAEA). Clamp latitudes to not go beyond the poles when calculating intervals. Further improve polar projections by checking if extent contains max and min intersection coordinates when calculating max and min. --- src/ol/layer/Graticule.js | 66 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 42b5c380fa..e7e61a7aba 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -16,6 +16,7 @@ import { } from '../proj.js'; import { applyTransform, + containsCoordinate, equals, getCenter, getHeight, @@ -285,6 +286,30 @@ class Graticule extends VectorLayer { */ this.projectionCenterLonLat_ = null; + /** + * @type {import("../coordinate.js").Coordinate} + * @private + */ + this.blLonLatP_ = null; + + /** + * @type {import("../coordinate.js").Coordinate} + * @private + */ + this.brLonLatP_ = null; + + /** + * @type {import("../coordinate.js").Coordinate} + * @private + */ + this.tlLonLatP_ = null; + + /** + * @type {import("../coordinate.js").Coordinate} + * @private + */ + this.trLonLatP_ = null; + /** * @type {Array} * @private @@ -679,19 +704,40 @@ class Graticule extends VectorLayer { ]; const centerLonLat = this.toLonLatTransform_(validCenter); + if (isNaN(centerLonLat[1])) { + centerLonLat[1] = Math.abs(this.maxLat_) >= Math.abs(this.minLat_) ? + this.maxLat_ : this.minLat_; + } let centerLon = clamp(centerLonLat[0], this.minLon_, this.maxLon_); let centerLat = clamp(centerLonLat[1], this.minLat_, this.maxLat_); const maxLines = this.maxLines_; let cnt, idx, lat, lon; - let validExtent = [ + const validExtentP = [ clamp(extent[0], this.minLonP_, this.maxLonP_), clamp(extent[1], this.minLatP_, this.maxLatP_), clamp(extent[2], this.minLonP_, this.maxLonP_), clamp(extent[3], this.minLatP_, this.maxLatP_) ]; - validExtent = applyTransform(validExtent, this.toLonLatTransform_, undefined, 8); + const validExtent = applyTransform(validExtentP, this.toLonLatTransform_, undefined, 8); + + if (containsCoordinate(validExtentP, this.blLonLatP_)) { + validExtent[0] = this.minLon_; + validExtent[1] = this.minLat_; + } + if (containsCoordinate(validExtentP, this.brLonLatP_)) { + validExtent[2] = this.maxLon_; + validExtent[1] = this.minLat_; + } + if (containsCoordinate(validExtentP, this.tlLonLatP_)) { + validExtent[0] = this.minLon_; + validExtent[3] = this.maxLat_; + } + if (containsCoordinate(validExtentP, this.trLonLatP_)) { + validExtent[2] = this.maxLon_; + validExtent[3] = this.maxLat_; + } const maxLat = clamp(validExtent[3], centerLat, this.maxLat_); const maxLon = clamp(validExtent[2], centerLon, this.maxLon_); @@ -767,11 +813,12 @@ class Graticule extends VectorLayer { /** @type {Array} **/ const p2 = []; for (let i = 0, ii = this.intervals_.length; i < ii; ++i) { - const delta = this.intervals_[i] / 2; + const delta = clamp(this.intervals_[i] / 2, 0, 90); + const clampedLat = clamp(centerLat, -90 + delta, 90 - delta); p1[0] = centerLon - delta; - p1[1] = centerLat - delta; + p1[1] = clampedLat - delta; p2[0] = centerLon + delta; - p2[1] = centerLat + delta; + p2[1] = clampedLat + delta; this.fromLonLatTransform_(p1, p1); this.fromLonLatTransform_(p2, p2); const dist = Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2); @@ -948,7 +995,16 @@ class Graticule extends VectorLayer { this.minLatP_ = worldExtentP[1]; this.minLonP_ = worldExtentP[0]; + this.blLonLatP_ = this.fromLonLatTransform_([this.minLon_, this.minLat_]); + this.brLonLatP_ = this.fromLonLatTransform_([this.maxLon_, this.minLat_]); + this.tlLonLatP_ = this.fromLonLatTransform_([this.minLon_, this.maxLat_]); + this.trLonLatP_ = this.fromLonLatTransform_([this.maxLon_, this.maxLat_]); + this.projectionCenterLonLat_ = this.toLonLatTransform_(getCenter(projection.getExtent())); + if (isNaN(this.projectionCenterLonLat_[1])) { + this.projectionCenterLonLat_[1] = Math.abs(this.maxLat_) >= Math.abs(this.minLat_) ? + this.maxLat_ : this.minLat_; + } this.projection_ = projection; } From 633396e7df71700364b2f343bfba0a51f9059f3c Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Thu, 27 Feb 2020 12:12:57 +0000 Subject: [PATCH 6/8] test using extra stops for extent transform --- test/spec/ol/extent.test.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index eb59120a2e..d98a0dfe61 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -1,5 +1,6 @@ import * as _ol_extent_ from '../../../src/ol/extent.js'; import {getTransform} from '../../../src/ol/proj.js'; +import {register} from '../../../src/ol/proj/proj4.js'; describe('ol.extent', function() { @@ -783,6 +784,38 @@ describe('ol.extent', function() { expect(destinationExtent[3]).to.be(30); }); + it('can use the stops option', function() { + proj4.defs('EPSG:32632', '+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs'); + register(proj4); + const transformFn = getTransform('EPSG:4326', 'EPSG:32632'); + const sourceExtentN = [6, 0, 12, 84]; + const destinationExtentN = _ol_extent_.applyTransform( + sourceExtentN, transformFn); + expect(destinationExtentN).not.to.be(undefined); + expect(destinationExtentN).not.to.be(null); + expect(destinationExtentN[0]).to.roughlyEqual(166021.44308053964, 1e-8); + expect(destinationExtentN[2]).to.roughlyEqual(0, 1e-8); + expect(destinationExtentN[1]).to.roughlyEqual(833978.5569194605, 1e-8); + expect(destinationExtentN[3]).to.roughlyEqual(9329005.182447437, 1e-8); + const sourceExtentNS = [6, -84, 12, 84]; + const destinationExtentNS = _ol_extent_.applyTransform( + sourceExtentNS, transformFn); + expect(destinationExtentNS).not.to.be(undefined); + expect(destinationExtentNS).not.to.be(null); + expect(destinationExtentNS[0]).to.roughlyEqual(465005.34493886377, 1e-8); + expect(destinationExtentNS[2]).to.roughlyEqual(-destinationExtentN[3], 1e-8); + expect(destinationExtentNS[1]).to.roughlyEqual(534994.6550611362, 1e-8); + expect(destinationExtentNS[3]).to.roughlyEqual(destinationExtentN[3], 1e-8); + const destinationExtentNS2 = _ol_extent_.applyTransform( + sourceExtentNS, transformFn, undefined, 2); + expect(destinationExtentNS2).not.to.be(undefined); + expect(destinationExtentNS2).not.to.be(null); + expect(destinationExtentNS2[0]).to.roughlyEqual(destinationExtentN[0], 1e-8); + expect(destinationExtentNS2[2]).to.roughlyEqual(-destinationExtentN[3], 1e-8); + expect(destinationExtentNS2[1]).to.roughlyEqual(destinationExtentN[2], 1e-8); + expect(destinationExtentNS2[3]).to.roughlyEqual(destinationExtentN[3], 1e-8); + }); + }); }); From ac2b58c554c07d4fc6a62cdf63a24231a178fca6 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Thu, 27 Feb 2020 12:19:31 +0000 Subject: [PATCH 7/8] test using extra stops for extent transform correct coordinate order --- test/spec/ol/extent.test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index d98a0dfe61..3e12639d19 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -794,8 +794,8 @@ describe('ol.extent', function() { expect(destinationExtentN).not.to.be(undefined); expect(destinationExtentN).not.to.be(null); expect(destinationExtentN[0]).to.roughlyEqual(166021.44308053964, 1e-8); - expect(destinationExtentN[2]).to.roughlyEqual(0, 1e-8); - expect(destinationExtentN[1]).to.roughlyEqual(833978.5569194605, 1e-8); + expect(destinationExtentN[2]).to.roughlyEqual(833978.5569194605, 1e-8); + expect(destinationExtentN[1]).to.roughlyEqual(0, 1e-8); expect(destinationExtentN[3]).to.roughlyEqual(9329005.182447437, 1e-8); const sourceExtentNS = [6, -84, 12, 84]; const destinationExtentNS = _ol_extent_.applyTransform( @@ -803,16 +803,16 @@ describe('ol.extent', function() { expect(destinationExtentNS).not.to.be(undefined); expect(destinationExtentNS).not.to.be(null); expect(destinationExtentNS[0]).to.roughlyEqual(465005.34493886377, 1e-8); - expect(destinationExtentNS[2]).to.roughlyEqual(-destinationExtentN[3], 1e-8); - expect(destinationExtentNS[1]).to.roughlyEqual(534994.6550611362, 1e-8); + expect(destinationExtentNS[2]).to.roughlyEqual(534994.6550611362, 1e-8); + expect(destinationExtentNS[1]).to.roughlyEqual(-destinationExtentN[3], 1e-8); expect(destinationExtentNS[3]).to.roughlyEqual(destinationExtentN[3], 1e-8); const destinationExtentNS2 = _ol_extent_.applyTransform( sourceExtentNS, transformFn, undefined, 2); expect(destinationExtentNS2).not.to.be(undefined); expect(destinationExtentNS2).not.to.be(null); expect(destinationExtentNS2[0]).to.roughlyEqual(destinationExtentN[0], 1e-8); - expect(destinationExtentNS2[2]).to.roughlyEqual(-destinationExtentN[3], 1e-8); - expect(destinationExtentNS2[1]).to.roughlyEqual(destinationExtentN[2], 1e-8); + expect(destinationExtentNS2[2]).to.roughlyEqual(destinationExtentN[2], 1e-8); + expect(destinationExtentNS2[1]).to.roughlyEqual(-destinationExtentN[3], 1e-8); expect(destinationExtentNS2[3]).to.roughlyEqual(destinationExtentN[3], 1e-8); }); From b8e34ef5bf2b9fb3f4faf3130d4b5ffbd01a6bbc Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Thu, 27 Feb 2020 19:12:40 +0000 Subject: [PATCH 8/8] rename some variables and add comments --- src/ol/layer/Graticule.js | 86 ++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index e7e61a7aba..0fbc0cea50 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -218,25 +218,25 @@ class Graticule extends VectorLayer { * @type {number} * @private */ - this.maxLatP_ = Infinity; + this.maxX_ = Infinity; /** * @type {number} * @private */ - this.maxLonP_ = Infinity; + this.maxY_ = Infinity; /** * @type {number} * @private */ - this.minLatP_ = -Infinity; + this.minX_ = -Infinity; /** * @type {number} * @private */ - this.minLonP_ = -Infinity; + this.minY_ = -Infinity; /** * @type {number} @@ -290,25 +290,25 @@ class Graticule extends VectorLayer { * @type {import("../coordinate.js").Coordinate} * @private */ - this.blLonLatP_ = null; + this.bottomLeft_ = null; /** * @type {import("../coordinate.js").Coordinate} * @private */ - this.brLonLatP_ = null; + this.bottomRight_ = null; /** * @type {import("../coordinate.js").Coordinate} * @private */ - this.tlLonLatP_ = null; + this.topLeft_ = null; /** * @type {import("../coordinate.js").Coordinate} * @private */ - this.trLonLatP_ = null; + this.topRight_ = null; /** * @type {Array} @@ -698,12 +698,18 @@ class Graticule extends VectorLayer { return; } - const validCenter = [ - clamp(center[0], this.minLonP_, this.maxLonP_), - clamp(center[1], this.minLatP_, this.maxLatP_) + // Constrain the center to fit into the extent available to the graticule + + const validCenterP = [ + clamp(center[0], this.minX_, this.maxX_), + clamp(center[1], this.minY_, this.maxY_) ]; - const centerLonLat = this.toLonLatTransform_(validCenter); + // Transform the center to lon lat + // Some projections may have a void area at the poles + // so replace any NaN latitudes with the min or max value closest to a pole + + const centerLonLat = this.toLonLatTransform_(validCenterP); if (isNaN(centerLonLat[1])) { centerLonLat[1] = Math.abs(this.maxLat_) >= Math.abs(this.minLat_) ? this.maxLat_ : this.minLat_; @@ -713,32 +719,42 @@ class Graticule extends VectorLayer { const maxLines = this.maxLines_; let cnt, idx, lat, lon; + // Limit the extent to fit into the extent available to the graticule + const validExtentP = [ - clamp(extent[0], this.minLonP_, this.maxLonP_), - clamp(extent[1], this.minLatP_, this.maxLatP_), - clamp(extent[2], this.minLonP_, this.maxLonP_), - clamp(extent[3], this.minLatP_, this.maxLatP_) + clamp(extent[0], this.minX_, this.maxX_), + clamp(extent[1], this.minY_, this.maxY_), + clamp(extent[2], this.minX_, this.maxX_), + clamp(extent[3], this.minY_, this.maxY_) ]; + // Transform the extent to get the lon lat ranges for the edges of the extent + const validExtent = applyTransform(validExtentP, this.toLonLatTransform_, undefined, 8); - if (containsCoordinate(validExtentP, this.blLonLatP_)) { + // Check if extremities of the world extent lie inside the extent + // (for example the pole in a polar projection) + // and extend the extent as appropriate + + if (containsCoordinate(validExtentP, this.bottomLeft_)) { validExtent[0] = this.minLon_; validExtent[1] = this.minLat_; } - if (containsCoordinate(validExtentP, this.brLonLatP_)) { + if (containsCoordinate(validExtentP, this.bottomRight_)) { validExtent[2] = this.maxLon_; validExtent[1] = this.minLat_; } - if (containsCoordinate(validExtentP, this.tlLonLatP_)) { + if (containsCoordinate(validExtentP, this.topLeft_)) { validExtent[0] = this.minLon_; validExtent[3] = this.maxLat_; } - if (containsCoordinate(validExtentP, this.trLonLatP_)) { + if (containsCoordinate(validExtentP, this.topRight_)) { validExtent[2] = this.maxLon_; validExtent[3] = this.maxLat_; } + // The transformed center may also extend the lon lat ranges used for rendering + const maxLat = clamp(validExtent[3], centerLat, this.maxLat_); const maxLon = clamp(validExtent[2], centerLon, this.maxLon_); const minLat = clamp(validExtent[1], this.minLat_, centerLat); @@ -814,6 +830,7 @@ class Graticule extends VectorLayer { const p2 = []; for (let i = 0, ii = this.intervals_.length; i < ii; ++i) { const delta = clamp(this.intervals_[i] / 2, 0, 90); + // Don't attempt to transform latitudes beyond the poles! const clampedLat = clamp(centerLat, -90 + delta, 90 - delta); p1[0] = centerLon - delta; p1[1] = clampedLat - delta; @@ -964,6 +981,9 @@ class Graticule extends VectorLayer { this.minLat_ = worldExtent[1]; this.minLon_ = worldExtent[0]; + // If the world extent crosses the dateline define a custom transform to + // return longitudes which wrap the dateline + const toLonLatTransform = getTransform(projection, epsg4326Projection); if (this.minLon_ < this.maxLon_) { this.toLonLatTransform_ = toLonLatTransform; @@ -982,6 +1002,9 @@ class Graticule extends VectorLayer { }; } + // Transform the extent to get the limits of the view projection extent + // which should be available to the graticule + this.fromLonLatTransform_ = getTransform(epsg4326Projection, projection); const worldExtentP = applyTransform( [this.minLon_, this.minLat_, this.maxLon_, this.maxLat_], @@ -990,15 +1013,22 @@ class Graticule extends VectorLayer { 8 ); - this.maxLatP_ = worldExtentP[3]; - this.maxLonP_ = worldExtentP[2]; - this.minLatP_ = worldExtentP[1]; - this.minLonP_ = worldExtentP[0]; + this.minX_ = worldExtentP[0]; + this.maxX_ = worldExtentP[2]; + this.minY_ = worldExtentP[1]; + this.maxY_ = worldExtentP[3]; - this.blLonLatP_ = this.fromLonLatTransform_([this.minLon_, this.minLat_]); - this.brLonLatP_ = this.fromLonLatTransform_([this.maxLon_, this.minLat_]); - this.tlLonLatP_ = this.fromLonLatTransform_([this.minLon_, this.maxLat_]); - this.trLonLatP_ = this.fromLonLatTransform_([this.maxLon_, this.maxLat_]); + // Determine the view projection coordinates of the extremities of the world extent + // as these may lie inside a view extent (for example the pole in a polar projection) + + this.bottomLeft_ = this.fromLonLatTransform_([this.minLon_, this.minLat_]); + this.bottomRight_ = this.fromLonLatTransform_([this.maxLon_, this.minLat_]); + this.topLeft_ = this.fromLonLatTransform_([this.minLon_, this.maxLat_]); + this.topRight_ = this.fromLonLatTransform_([this.maxLon_, this.maxLat_]); + + // Transform the projection center to lon lat + // Some projections may have a void area at the poles + // so replace any NaN latitudes with the min or max value closest to a pole this.projectionCenterLonLat_ = this.toLonLatTransform_(getCenter(projection.getExtent())); if (isNaN(this.projectionCenterLonLat_[1])) {