From a392b74992671dec1f3c75498bd0b406bf85aab9 Mon Sep 17 00:00:00 2001 From: Alexander Wunschik Date: Mon, 5 Oct 2020 17:15:12 +0200 Subject: [PATCH 1/7] feat(geo): support min/max scale limits fixes #5191 --- src/components/modebar/buttons.js | 10 ++++++++++ src/plots/geo/geo.js | 8 ++++++++ src/plots/geo/layout_attributes.js | 20 ++++++++++++++++++++ src/plots/geo/layout_defaults.js | 4 ++++ src/plots/geo/zoom.js | 1 + 5 files changed, 43 insertions(+) diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 67bc00abf55..248ab0afbb8 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -548,8 +548,18 @@ function handleGeo(gd, ev) { if(attr === 'zoom') { var scale = geoLayout.projection.scale; + var minScale = geoLayout.projection.minScale; + var maxScale = geoLayout.projection.maxScale; + var newScale = (val === 'in') ? 2 * scale : 0.5 * scale; + // make sure the scale is within the min/max bounds + if(newScale > maxScale) { + newScale = maxScale; + } else if(newScale < minScale) { + newScale = minScale; + } + Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale); } } diff --git a/src/plots/geo/geo.js b/src/plots/geo/geo.js index db7a2d83564..594cb073335 100644 --- a/src/plots/geo/geo.js +++ b/src/plots/geo/geo.js @@ -709,6 +709,14 @@ function getProjection(geoLayout) { projection.precision(constants.precision); + // https://github.com/d3/d3-zoom/blob/master/README.md#zoom_scaleExtent + projection.scaleExtent = function() { + var minscale = projLayout.minscale; + var maxscale = projLayout.maxscale; + if(maxscale === -1) maxscale = Infinity; + return [100 * minscale, 100 * maxscale]; + }; + if(geoLayout._isSatellite) { projection.tilt(projLayout.tilt).distance(projLayout.distance); } diff --git a/src/plots/geo/layout_attributes.js b/src/plots/geo/layout_attributes.js index 97ef64786e6..d8a087a290d 100644 --- a/src/plots/geo/layout_attributes.js +++ b/src/plots/geo/layout_attributes.js @@ -177,6 +177,26 @@ var attrs = module.exports = overrideAll({ 'that fits the map\'s lon and lat ranges. ' ].join(' ') }, + minScale: { + valType: 'number', + min: 0, + dflt: 0, + description: [ + 'Minimal zoom level of the map view.', + 'A minScale of *0.5* (50%) corresponds to a zoom level', + 'where the map has half the size of base zoom level.' + ].join(' ') + }, + maxScale: { + valType: 'number', + min: 0, + dflt: Infinity, + description: [ + 'Maximal zoom level of the map view.', + 'A maxScale of *2* (200%) corresponds to a zoom level', + 'where the map is twice as big as the base layer.' + ].join(' ') + }, }, center: { lon: { diff --git a/src/plots/geo/layout_defaults.js b/src/plots/geo/layout_defaults.js index 54b4d49bda4..2added2d865 100644 --- a/src/plots/geo/layout_defaults.js +++ b/src/plots/geo/layout_defaults.js @@ -161,6 +161,8 @@ function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce, opts) { } coerce('projection.scale'); + coerce('projection.minScale'); + coerce('projection.maxScale'); show = coerce('showland', !visible ? false : undefined); if(show) coerce('landcolor'); @@ -205,6 +207,8 @@ function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce, opts) { // clear attributes that will get auto-filled later if(fitBounds) { delete geoLayoutOut.projection.scale; + delete geoLayoutOut.projection.minScale; + delete geoLayoutOut.projection.maxScale; if(isScoped) { delete geoLayoutOut.center.lon; diff --git a/src/plots/geo/zoom.js b/src/plots/geo/zoom.js index 2d79d69f581..824ba39a0ef 100644 --- a/src/plots/geo/zoom.js +++ b/src/plots/geo/zoom.js @@ -32,6 +32,7 @@ module.exports = createGeoZoom; function initZoom(geo, projection) { return d3.behavior.zoom() .translate(projection.translate()) + .scaleExtent(projection.scaleExtent()) .scale(projection.scale()); } From df23838cacc4d8b9ed7fe2e1e8cbba33c89056a5 Mon Sep 17 00:00:00 2001 From: Alexander Wunschik Date: Mon, 26 Oct 2020 20:11:21 +0100 Subject: [PATCH 2/7] refactor: rename min/max scale limits --- src/components/modebar/buttons.js | 12 ++++++------ src/plots/geo/layout_attributes.js | 6 +++--- src/plots/geo/layout_defaults.js | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 248ab0afbb8..d5a8510bfa3 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -548,16 +548,16 @@ function handleGeo(gd, ev) { if(attr === 'zoom') { var scale = geoLayout.projection.scale; - var minScale = geoLayout.projection.minScale; - var maxScale = geoLayout.projection.maxScale; + var minscale = geoLayout.projection.minscale; + var maxscale = geoLayout.projection.maxscale; var newScale = (val === 'in') ? 2 * scale : 0.5 * scale; // make sure the scale is within the min/max bounds - if(newScale > maxScale) { - newScale = maxScale; - } else if(newScale < minScale) { - newScale = minScale; + if(newScale > maxscale) { + newScale = maxscale; + } else if(newScale < minscale) { + newScale = minscale; } Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale); diff --git a/src/plots/geo/layout_attributes.js b/src/plots/geo/layout_attributes.js index d8a087a290d..6c66262c3e7 100644 --- a/src/plots/geo/layout_attributes.js +++ b/src/plots/geo/layout_attributes.js @@ -177,7 +177,7 @@ var attrs = module.exports = overrideAll({ 'that fits the map\'s lon and lat ranges. ' ].join(' ') }, - minScale: { + minscale: { valType: 'number', min: 0, dflt: 0, @@ -187,10 +187,10 @@ var attrs = module.exports = overrideAll({ 'where the map has half the size of base zoom level.' ].join(' ') }, - maxScale: { + maxscale: { valType: 'number', min: 0, - dflt: Infinity, + dflt: null, description: [ 'Maximal zoom level of the map view.', 'A maxScale of *2* (200%) corresponds to a zoom level', diff --git a/src/plots/geo/layout_defaults.js b/src/plots/geo/layout_defaults.js index 2added2d865..411a42cfdb1 100644 --- a/src/plots/geo/layout_defaults.js +++ b/src/plots/geo/layout_defaults.js @@ -161,8 +161,8 @@ function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce, opts) { } coerce('projection.scale'); - coerce('projection.minScale'); - coerce('projection.maxScale'); + coerce('projection.minscale'); + coerce('projection.maxscale'); show = coerce('showland', !visible ? false : undefined); if(show) coerce('landcolor'); @@ -207,8 +207,8 @@ function handleGeoDefaults(geoLayoutIn, geoLayoutOut, coerce, opts) { // clear attributes that will get auto-filled later if(fitBounds) { delete geoLayoutOut.projection.scale; - delete geoLayoutOut.projection.minScale; - delete geoLayoutOut.projection.maxScale; + delete geoLayoutOut.projection.minscale; + delete geoLayoutOut.projection.maxscale; if(isScoped) { delete geoLayoutOut.center.lon; From 388c0b0072f11d4077b246cde1a6f8ebc12e0627 Mon Sep 17 00:00:00 2001 From: Alexander Wunschik Date: Mon, 26 Oct 2020 20:43:18 +0100 Subject: [PATCH 3/7] chore: use dflt: -1 instad of null --- src/components/modebar/buttons.js | 1 + src/plots/geo/layout_attributes.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index d5a8510bfa3..6bf2ddcf817 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -551,6 +551,7 @@ function handleGeo(gd, ev) { var minscale = geoLayout.projection.minscale; var maxscale = geoLayout.projection.maxscale; + if(maxscale < 0) maxscale = Infinity; var newScale = (val === 'in') ? 2 * scale : 0.5 * scale; // make sure the scale is within the min/max bounds diff --git a/src/plots/geo/layout_attributes.js b/src/plots/geo/layout_attributes.js index 6c66262c3e7..f4e60353e3a 100644 --- a/src/plots/geo/layout_attributes.js +++ b/src/plots/geo/layout_attributes.js @@ -190,7 +190,7 @@ var attrs = module.exports = overrideAll({ maxscale: { valType: 'number', min: 0, - dflt: null, + dflt: -1, description: [ 'Maximal zoom level of the map view.', 'A maxScale of *2* (200%) corresponds to a zoom level', From d531576456f446d7d0ab8bfe10833ee216df81bc Mon Sep 17 00:00:00 2001 From: Alexander Wunschik Date: Mon, 26 Oct 2020 21:26:40 +0100 Subject: [PATCH 4/7] feat: handle maxscale default in scaleExtent --- src/components/modebar/buttons.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 6bf2ddcf817..d0106cfdc26 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -551,7 +551,7 @@ function handleGeo(gd, ev) { var minscale = geoLayout.projection.minscale; var maxscale = geoLayout.projection.maxscale; - if(maxscale < 0) maxscale = Infinity; + if(maxscale === -1) maxscale = Infinity; var newScale = (val === 'in') ? 2 * scale : 0.5 * scale; // make sure the scale is within the min/max bounds From b0daf3899fda9ef670cf3906ecc08043ee52850a Mon Sep 17 00:00:00 2001 From: Alexander Wunschik Date: Mon, 26 Oct 2020 22:39:43 +0100 Subject: [PATCH 5/7] chore: only rerender on zoom if scale changes --- src/components/modebar/buttons.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index d0106cfdc26..a1bef00d5b3 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -561,7 +561,9 @@ function handleGeo(gd, ev) { newScale = minscale; } - Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale); + if(newScale !== scale) { + Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale); + } } } From b46375a24c1377084e67ddee6b2aa5811e52b3a5 Mon Sep 17 00:00:00 2001 From: Alexander Wunschik Date: Mon, 26 Oct 2020 22:40:26 +0100 Subject: [PATCH 6/7] chore: fix typo in min/maxscale attributes --- src/plots/geo/layout_attributes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plots/geo/layout_attributes.js b/src/plots/geo/layout_attributes.js index f4e60353e3a..f320976dbe8 100644 --- a/src/plots/geo/layout_attributes.js +++ b/src/plots/geo/layout_attributes.js @@ -183,7 +183,7 @@ var attrs = module.exports = overrideAll({ dflt: 0, description: [ 'Minimal zoom level of the map view.', - 'A minScale of *0.5* (50%) corresponds to a zoom level', + 'A minscale of *0.5* (50%) corresponds to a zoom level', 'where the map has half the size of base zoom level.' ].join(' ') }, @@ -193,7 +193,7 @@ var attrs = module.exports = overrideAll({ dflt: -1, description: [ 'Maximal zoom level of the map view.', - 'A maxScale of *2* (200%) corresponds to a zoom level', + 'A maxscale of *2* (200%) corresponds to a zoom level', 'where the map is twice as big as the base layer.' ].join(' ') }, From c42cb9d3003b58bcac983ada71f2f43a1ad85692 Mon Sep 17 00:00:00 2001 From: Alexander Wunschik Date: Mon, 10 Jun 2024 23:52:30 +0200 Subject: [PATCH 7/7] test: adopt test/plot-schema.json to new plot-schema --- test/plot-schema.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/plot-schema.json b/test/plot-schema.json index fd2b1659e37..eb189f22bbb 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -2585,6 +2585,20 @@ "valType": "number" }, "editType": "plot", + "maxscale": { + "description": "Maximal zoom level of the map view. A maxscale of *2* (200%) corresponds to a zoom level where the map is twice as big as the base layer.", + "dflt": -1, + "editType": "plot", + "min": 0, + "valType": "number" + }, + "minscale": { + "description": "Minimal zoom level of the map view. A minscale of *0.5* (50%) corresponds to a zoom level where the map has half the size of base zoom level.", + "dflt": 0, + "editType": "plot", + "min": 0, + "valType": "number" + }, "parallels": { "description": "For conic projection types only. Sets the parallels (tangent, secant) where the cone intersects the sphere.", "editType": "plot",