Skip to content

Commit

Permalink
handle invalid geo setting that lead to invalid bounds
Browse files Browse the repository at this point in the history
- in that case, call Plotly.relayout to reset all
  attributes that could cause the invalid bounds.
  • Loading branch information
etpinard committed Sep 27, 2017
1 parent 1c26ec4 commit 1d2ce2d
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 9 deletions.
39 changes: 30 additions & 9 deletions src/plots/geo/geo.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ function Geo(opts) {
this.topojson = null;

this.projection = null;
this.viewInitial = null;
this.fitScale = null;
this.bounds = null;
this.midPt = null;
Expand Down Expand Up @@ -119,6 +120,9 @@ proto.fetchTopojson = function() {
proto.update = function(geoCalcData, fullLayout) {
var geoLayout = fullLayout[this.id];

var hasInvalidBounds = this.updateProjection(fullLayout, geoLayout);
if(hasInvalidBounds) return;

// important: maps with choropleth traces have a different layer order
this.hasChoropleth = false;
for(var i = 0; i < geoCalcData.length; i++) {
Expand All @@ -128,9 +132,13 @@ proto.update = function(geoCalcData, fullLayout) {
}
}

this.updateProjection(fullLayout, geoLayout);
if(!this.viewInitial) {
this.saveViewInitial(geoLayout);
}

this.updateBaseLayers(fullLayout, geoLayout);
this.updateDims(fullLayout, geoLayout);
this.updateFx(fullLayout, geoLayout);

Plots.generalUpdatePerTraceModule(this, geoCalcData, geoLayout);

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

this.updateFx(fullLayout, geoLayout);
this.render();
};

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

// TODO fallback to default ???
// clear all attribute that could cause invalid bounds,
// clear viewInitial to update reset-view behavior

for(var i = 0; i < attrToUnset.length; i++) {
updateObj[this.id + '.' + attrToUnset[i]] = null;
}

this.viewInitial = null;

Lib.warn(msg);
gd._promises.push(Plotly.relayout(gd, updateObj));
return msg;
}

// px coordinates of view mid-point,
Expand Down Expand Up @@ -472,26 +493,27 @@ proto.makeFramework = function() {
exponentformat: 'B'
};
Axes.setConvert(_this.mockAxis, fullLayout);
};

var geoLayout = fullLayout[_this.id];
proto.saveViewInitial = function(geoLayout) {
var center = geoLayout.center || {};
var projLayout = geoLayout.projection;
var rotation = projLayout.rotation || {};

if(geoLayout._isScoped) {
_this.viewInitial = {
this.viewInitial = {
'center.lon': center.lon,
'center.lat': center.lat,
'projection.scale': projLayout.scale
};
} else if(geoLayout._isClipped) {
_this.viewInitial = {
this.viewInitial = {
'projection.scale': projLayout.scale,
'projection.rotation.lon': rotation.lon,
'projection.rotation.lat': rotation.lat
};
} else {
_this.viewInitial = {
this.viewInitial = {
'center.lon': center.lon,
'center.lat': center.lat,
'projection.scale': projLayout.scale,
Expand Down Expand Up @@ -574,7 +596,6 @@ function getProjection(geoLayout) {
var maxAngle = clipAngle * Math.PI / 180;
return angle > maxAngle;
} else {
// TODO does this ever happen??
return false;
}
};
Expand Down
59 changes: 59 additions & 0 deletions test/jasmine/tests/geo_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,65 @@ describe('Test geo interactions', function() {
.then(done);
});

it('should plot to scope defaults when user setting lead to NaN map bounds', function(done) {
var gd = createGraphDiv();

spyOn(Lib, 'warn');

Plotly.plot(gd, [{
type: 'scattergeo',
lon: [0],
lat: [0]
}], {
geo: {
projection: {
type: 'kavrayskiy7',
rotation: {
lat: 38.794799,
lon: -81.622334,
}
},
center: {
lat: -81
},
lataxis: {
range: [38.794799, 45.122292]
},
lonaxis: {
range: [-82.904731, -81.622334]
}
},
width: 700,
heigth: 500
})
.then(function() {
var geoLayout = gd._fullLayout.geo;
var geo = geoLayout._subplot;

expect(geoLayout.projection.rotation).toEqual({
lon: 0, lat: 0, roll: 0,
});
expect(geoLayout.center).toEqual({
lon: 0, lat: 0
});
expect(geoLayout.lonaxis.range).toEqual([-180, 180]);
expect(geoLayout.lataxis.range).toEqual([-90, 90]);

expect(geo.viewInitial).toEqual({
'projection.rotation.lon': 0,
'center.lon': 0,
'center.lat': 0,
'projection.scale': 1
});

expect(Lib.warn).toHaveBeenCalledTimes(1);
expect(Lib.warn).toHaveBeenCalledWith(
'Invalid geo settings, relayout\'ing to default view.'
);
})
.catch(fail)
.then(done);
});
});

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

0 comments on commit 1d2ce2d

Please sign in to comment.