Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add min/max scale limits for geo plots #7371

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions draftlogs/7371_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add `minscale`, `maxscale` geo plot attributes [[#7371](https://github.com/plotly/plotly.js/pull/7371)]
15 changes: 14 additions & 1 deletion src/components/modebar/buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -543,9 +543,22 @@ function handleGeo(gd, ev) {

if(attr === 'zoom') {
var scale = geoLayout.projection.scale;
var minscale = geoLayout.projection.minscale;
var maxscale = geoLayout.projection.maxscale === -1 ? Infinity : geoLayout.projection.maxscale;
var max = Math.max(minscale, maxscale);
var min = Math.min(minscale, maxscale);
var newScale = (val === 'in') ? 2 * scale : 0.5 * scale;

Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale);
// make sure the scale is within the min/max bounds
if (newScale > max) {
newScale = max;
} else if (newScale < min) {
newScale = min;
}

if (newScale !== scale) {
Registry.call('_guiRelayout', gd, id + '.projection.scale', newScale);
}
}
}

Expand Down
15 changes: 14 additions & 1 deletion src/plots/geo/geo.js
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,11 @@ proto.updateFx = function(fullLayout, geoLayout) {

if(dragMode === 'pan') {
bgRect.node().onmousedown = null;
bgRect.call(createGeoZoom(_this, geoLayout));
var zoom = createGeoZoom(_this, geoLayout)
bgRect.call(zoom);
// TODO: Figure out how to restrict when this transition occurs. Or is it a no-op if nothing has changed?
// Trigger transition to handle if minscale attribute isn't 0
zoom.event(bgRect)
bgRect.on('dblclick.zoom', zoomReset);
if(!gd._context._scrollZoom.geo) {
bgRect.on('wheel.zoom', null);
Expand Down Expand Up @@ -709,6 +713,15 @@ function getProjection(geoLayout) {

projection.precision(constants.precision);

// https://d3js.org/d3-zoom#zoom_scaleExtent
projection.scaleExtent = () => {
var minscale = projLayout.minscale;
var maxscale = projLayout.maxscale === -1 ? Infinity : projLayout.maxscale;
var max = Math.max(minscale, maxscale);
var min = Math.min(minscale, maxscale);
return [100 * min, 100 * max];
};

if(geoLayout._isSatellite) {
projection.tilt(projLayout.tilt).distance(projLayout.distance);
}
Expand Down
20 changes: 20 additions & 0 deletions src/plots/geo/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: -1,
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: {
Expand Down
4 changes: 4 additions & 0 deletions src/plots/geo/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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;
Expand Down
22 changes: 17 additions & 5 deletions src/plots/geo/zoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = createGeoZoom;
function initZoom(geo, projection) {
return d3.behavior.zoom()
.translate(projection.translate())
.scaleExtent(projection.scaleExtent())
.scale(projection.scale());
}

Expand Down Expand Up @@ -132,16 +133,21 @@ function zoomNonClipped(geo, projection) {
function handleZoomstart() {
d3.select(this).style(zoomstartStyle);

mouse0 = d3.mouse(this);
var rect = this.getBBox()
mouse0 = d3.event.sourceEvent
? d3.mouse(this)
: [rect.x + rect.width / 2, rect.y + rect.height / 2];
rotate0 = projection.rotate();
translate0 = projection.translate();
lastRotate = rotate0;
zoomPoint = position(mouse0);
}

function handleZoom() {
mouse1 = d3.mouse(this);

var rect = this.getBBox()
mouse1 = d3.event.sourceEvent
? d3.mouse(this)
: [rect.x + rect.width / 2, rect.y + rect.height / 2];
if(outside(mouse0)) {
zoom.scale(projection.scale());
zoom.translate(projection.translate());
Expand Down Expand Up @@ -210,7 +216,10 @@ function zoomClipped(geo, projection) {
zoom.on('zoomstart', function() {
d3.select(this).style(zoomstartStyle);

var mouse0 = d3.mouse(this);
var rect = this.getBBox()
var mouse0 = d3.event.sourceEvent
? d3.mouse(this)
: [rect.x + rect.width / 2, rect.y + rect.height / 2];
var rotate0 = projection.rotate();
var lastRotate = rotate0;
var translate0 = projection.translate();
Expand All @@ -219,7 +228,10 @@ function zoomClipped(geo, projection) {
zoomPoint = position(projection, mouse0);

zoomOn.call(zoom, 'zoom', function() {
var mouse1 = d3.mouse(this);
var rect = this.getBBox()
var mouse1 = d3.event.sourceEvent
? d3.mouse(this)
: [rect.x + rect.width / 2, rect.y + rect.height / 2];

projection.scale(view.k = d3.event.scale);

Expand Down
14 changes: 14 additions & 0 deletions test/plot-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2373,6 +2373,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",
Expand Down