Skip to content

Commit 1d2ce2d

Browse files
committed
handle invalid geo setting that lead to invalid bounds
- in that case, call Plotly.relayout to reset all attributes that could cause the invalid bounds.
1 parent 1c26ec4 commit 1d2ce2d

File tree

2 files changed

+89
-9
lines changed

2 files changed

+89
-9
lines changed

src/plots/geo/geo.js

+30-9
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ function Geo(opts) {
4141
this.topojson = null;
4242

4343
this.projection = null;
44+
this.viewInitial = null;
4445
this.fitScale = null;
4546
this.bounds = null;
4647
this.midPt = null;
@@ -119,6 +120,9 @@ proto.fetchTopojson = function() {
119120
proto.update = function(geoCalcData, fullLayout) {
120121
var geoLayout = fullLayout[this.id];
121122

123+
var hasInvalidBounds = this.updateProjection(fullLayout, geoLayout);
124+
if(hasInvalidBounds) return;
125+
122126
// important: maps with choropleth traces have a different layer order
123127
this.hasChoropleth = false;
124128
for(var i = 0; i < geoCalcData.length; i++) {
@@ -128,9 +132,13 @@ proto.update = function(geoCalcData, fullLayout) {
128132
}
129133
}
130134

131-
this.updateProjection(fullLayout, geoLayout);
135+
if(!this.viewInitial) {
136+
this.saveViewInitial(geoLayout);
137+
}
138+
132139
this.updateBaseLayers(fullLayout, geoLayout);
133140
this.updateDims(fullLayout, geoLayout);
141+
this.updateFx(fullLayout, geoLayout);
134142

135143
Plots.generalUpdatePerTraceModule(this, geoCalcData, geoLayout);
136144

@@ -142,7 +150,6 @@ proto.update = function(geoCalcData, fullLayout) {
142150
var choroplethLayer = this.layers.backplot.select('.choroplethlayer');
143151
this.dataPaths.choropleth = choroplethLayer.selectAll('path');
144152

145-
this.updateFx(fullLayout, geoLayout);
146153
this.render();
147154
};
148155

@@ -186,9 +193,23 @@ proto.updateProjection = function(fullLayout, geoLayout) {
186193
!isFinite(b[1][0]) || !isFinite(b[1][1]) ||
187194
isNaN(t[0]) || isNaN(t[0])
188195
) {
189-
Lib.warn('Invalid geo settings');
196+
var gd = this.graphDiv;
197+
var attrToUnset = ['projection.rotation', 'center', 'lonaxis.range', 'lataxis.range'];
198+
var msg = 'Invalid geo settings, relayout\'ing to default view.';
199+
var updateObj = {};
190200

191-
// TODO fallback to default ???
201+
// clear all attribute that could cause invalid bounds,
202+
// clear viewInitial to update reset-view behavior
203+
204+
for(var i = 0; i < attrToUnset.length; i++) {
205+
updateObj[this.id + '.' + attrToUnset[i]] = null;
206+
}
207+
208+
this.viewInitial = null;
209+
210+
Lib.warn(msg);
211+
gd._promises.push(Plotly.relayout(gd, updateObj));
212+
return msg;
192213
}
193214

194215
// px coordinates of view mid-point,
@@ -472,26 +493,27 @@ proto.makeFramework = function() {
472493
exponentformat: 'B'
473494
};
474495
Axes.setConvert(_this.mockAxis, fullLayout);
496+
};
475497

476-
var geoLayout = fullLayout[_this.id];
498+
proto.saveViewInitial = function(geoLayout) {
477499
var center = geoLayout.center || {};
478500
var projLayout = geoLayout.projection;
479501
var rotation = projLayout.rotation || {};
480502

481503
if(geoLayout._isScoped) {
482-
_this.viewInitial = {
504+
this.viewInitial = {
483505
'center.lon': center.lon,
484506
'center.lat': center.lat,
485507
'projection.scale': projLayout.scale
486508
};
487509
} else if(geoLayout._isClipped) {
488-
_this.viewInitial = {
510+
this.viewInitial = {
489511
'projection.scale': projLayout.scale,
490512
'projection.rotation.lon': rotation.lon,
491513
'projection.rotation.lat': rotation.lat
492514
};
493515
} else {
494-
_this.viewInitial = {
516+
this.viewInitial = {
495517
'center.lon': center.lon,
496518
'center.lat': center.lat,
497519
'projection.scale': projLayout.scale,
@@ -574,7 +596,6 @@ function getProjection(geoLayout) {
574596
var maxAngle = clipAngle * Math.PI / 180;
575597
return angle > maxAngle;
576598
} else {
577-
// TODO does this ever happen??
578599
return false;
579600
}
580601
};

test/jasmine/tests/geo_test.js

+59
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,65 @@ describe('Test geo interactions', function() {
12311231
.then(done);
12321232
});
12331233

1234+
it('should plot to scope defaults when user setting lead to NaN map bounds', function(done) {
1235+
var gd = createGraphDiv();
1236+
1237+
spyOn(Lib, 'warn');
1238+
1239+
Plotly.plot(gd, [{
1240+
type: 'scattergeo',
1241+
lon: [0],
1242+
lat: [0]
1243+
}], {
1244+
geo: {
1245+
projection: {
1246+
type: 'kavrayskiy7',
1247+
rotation: {
1248+
lat: 38.794799,
1249+
lon: -81.622334,
1250+
}
1251+
},
1252+
center: {
1253+
lat: -81
1254+
},
1255+
lataxis: {
1256+
range: [38.794799, 45.122292]
1257+
},
1258+
lonaxis: {
1259+
range: [-82.904731, -81.622334]
1260+
}
1261+
},
1262+
width: 700,
1263+
heigth: 500
1264+
})
1265+
.then(function() {
1266+
var geoLayout = gd._fullLayout.geo;
1267+
var geo = geoLayout._subplot;
1268+
1269+
expect(geoLayout.projection.rotation).toEqual({
1270+
lon: 0, lat: 0, roll: 0,
1271+
});
1272+
expect(geoLayout.center).toEqual({
1273+
lon: 0, lat: 0
1274+
});
1275+
expect(geoLayout.lonaxis.range).toEqual([-180, 180]);
1276+
expect(geoLayout.lataxis.range).toEqual([-90, 90]);
1277+
1278+
expect(geo.viewInitial).toEqual({
1279+
'projection.rotation.lon': 0,
1280+
'center.lon': 0,
1281+
'center.lat': 0,
1282+
'projection.scale': 1
1283+
});
1284+
1285+
expect(Lib.warn).toHaveBeenCalledTimes(1);
1286+
expect(Lib.warn).toHaveBeenCalledWith(
1287+
'Invalid geo settings, relayout\'ing to default view.'
1288+
);
1289+
})
1290+
.catch(fail)
1291+
.then(done);
1292+
});
12341293
});
12351294

12361295
describe('Test event property of interactions on a geo plot:', function() {

0 commit comments

Comments
 (0)