Skip to content

Commit

Permalink
fix choropleth hover for countries w/ have polygons that cross -180
Browse files Browse the repository at this point in the history
- these countries are Russia and Fiji.
- Note that other countries have polygons on either side of
  the antimeridian (e.g. some Aleutian island for the USA),
  but those don't confuse the 'contains' method.
  • Loading branch information
etpinard committed Sep 27, 2017
1 parent 1d2ce2d commit 651399e
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/traces/choropleth/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ module.exports = function hoverPoints(pointData, xval, yval) {
if(pt._polygons[j].contains([xval, yval])) {
isInside = !isInside;
}
// for polygons that cross antimeridian as xval is in [-180, 180]
if(pt._polygons[j].contains([xval + 360, yval])) {
isInside = !isInside;
}
}

if(isInside) break;
Expand Down
21 changes: 20 additions & 1 deletion src/traces/choropleth/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function calcGeoJSON(calcTrace, topojson) {
var trace = calcTrace[0].trace;
var len = calcTrace.length;
var features = getTopojsonFeatures(trace, topojson);
var i, j, k;
var i, j, k, m;

for(i = 0; i < len; i++) {
var calcPt = calcTrace[i];
Expand All @@ -74,6 +74,25 @@ function calcGeoJSON(calcTrace, topojson) {
var coords = geometry.coordinates;
calcPt._polygons = [];

// Russia and Fiji have landmasses that cross the antimeridian,
// we need to add +360 to their longitude coordinates, so that
// polygon 'contains' doesn't get confused when crossing the antimeridian.
//
// Note that other countries have polygons on either side of the antimeridian
// (e.g. some Aleutian island for the USA), but those don't confuse
// the 'contains' method; these are skipped here.
if(calcPt.loc === 'RUS' || calcPt.loc === 'FJI') {
for(j = 0; j < coords.length; j++) {
for(k = 0; k < coords[j].length; k++) {
for(m = 0; m < coords[j][k].length; m++) {
if(coords[j][k][m][0] < 0) {
coords[j][k][m][0] += 360;
}
}
}
}
}

This comment has been minimized.

Copy link
@alexcjohnson

alexcjohnson Sep 27, 2017

Collaborator

Nice sleuthing! Good thing Russia doesn't also cross the prime meridian! I wonder though, is there a way we could do this at the build stage so we don't need this special handling while plotting?

This comment has been minimized.

Copy link
@etpinard

etpinard Sep 27, 2017

Author Contributor

Good call. See etpinard/sane-topojson#8


switch(geometry.type) {
case 'MultiPolygon':
for(j = 0; j < coords.length; j++) {
Expand Down
40 changes: 40 additions & 0 deletions test/jasmine/tests/geo_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,46 @@ describe('Test geo interactions', function() {
.catch(fail)
.then(done);
});

it('should get hover right for choropleths involving landmasses that cross antimeridian', function(done) {
var gd = createGraphDiv();

function check(lonlat, hoverLabelCnt, msg) {
var projection = gd._fullLayout.geo._subplot.projection;
var px = projection(lonlat);

mouseEvent('mousemove', px[0], px[1]);
expect(d3.selectAll('g.hovertext').size()).toBe(hoverLabelCnt, msg);

delete gd._lastHoverTime;
}

Plotly.newPlot(gd, [{
type: 'choropleth',
locations: ['RUS', 'FJI'],
z: [0, 1]
}])
.then(function() {
check([81, 66], 1, 'spot in north-central Russia that polygon.contains gets wrong before +360 shift');
check([-80, 66], 0, 'spot north of Hudson bay that polygon.contains believe is in Russia before before +360 shift');

return Plotly.relayout(gd, 'geo.projection.rotation.lon', 180);
})
.then(function() {
check([-174, 65], 1, 'spot in Russia mainland beyond antimeridian');

return Plotly.relayout(gd, {
'geo.center.lat': -16,
'geo.projection.scale': 17
});
})
.then(function() {
check([179, -16.6], 1, 'spot on Fiji island that cross antimeridian west of antimeridian');
check([-179.9, -16.2], 1, 'spot on Fiji island that cross antimeridian east of antimeridian');
})
.catch(fail)
.then(done);
});
});

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

0 comments on commit 651399e

Please sign in to comment.