Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions draftlogs/7905_fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Fix `scattermap` box and lasso selection across the antimeridian [[#7905](https://github.com/plotly/plotly.js/pull/7905)]
10 changes: 9 additions & 1 deletion src/plots/map/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,15 @@ proto.addLayer = function(opts, below) {

// convenience method to project a [lon, lat] array to pixel coords
proto.project = function(v) {
return this.map.project(new maplibregl.LngLat(v[0], v[1]));
var map = this.map;
var lon = v[0];

if(map.getRenderWorldCopies()) {
var centerLon = map.getCenter().lng;
lon = centerLon + Lib.modHalf(lon - centerLon, 360);
}

return map.project(new maplibregl.LngLat(lon, v[1]));
};

// get map's current view values in plotly.js notation
Expand Down
81 changes: 81 additions & 0 deletions test/jasmine/tests/select_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2276,6 +2276,87 @@ describe('Test select box and lasso per trace:', function() {
}, LONG_TIMEOUT_INTERVAL);
});

it('@gl should select scattermap points across the antimeridian', function(done) {
var lons = [174.76, 178.44, 179.9, -176.2, -171.77, -175.2];
var lats = [-36.85, -18.14, -16.5, -13.3, -13.83, -21.14];
var expectedPoints = lons.map(function(lon, i) { return [lon, lats[i]]; });
var assertPoints = makeAssertPoints(['lon', 'lat']);
var assertSelectedPoints = makeAssertSelectedPoints();
var boxPath;
var lassoPath;

var fig = {
data: [{
type: 'scattermap',
mode: 'markers',
lon: lons,
lat: lats,
marker: {size: 10}
}],
layout: {
dragmode: 'select',
width: 900,
height: 600,
map: {
center: {lon: 180, lat: -24},
style: 'white-bg',
zoom: 3
}
},
config: {}
};

_newPlot(gd, fig)
.then(function() {
var subplot = gd._fullLayout.map._subplot;
var points = lons.map(function(lon, i) {
var unwrappedLon = lon < 0 ? lon + 360 : lon;
var pt = subplot.map.project([unwrappedLon, lats[i]]);
return [pt.x + subplot.xaxis._offset, pt.y + subplot.yaxis._offset];
});
var xs = points.map(function(pt) { return pt[0]; });
var ys = points.map(function(pt) { return pt[1]; });
var x0 = Math.min.apply(null, xs) - 10;
var x1 = Math.max.apply(null, xs) + 10;
var y0 = Math.min.apply(null, ys) - 10;
var y1 = Math.max.apply(null, ys) + 10;

boxPath = [[x0, y0], [x1, y1]];
lassoPath = [[x0, y0], [x0, y1], [x1, y1], [x1, y0], [x0, y0]];

return _run(false, boxPath,
function() {
assertPoints(expectedPoints);
assertSelectedPoints({0: [0, 1, 2, 3, 4, 5]});

var range = selectedData.range.map;
expect(range[1][0]).toBeGreaterThan(range[0][0], 'continuous longitude range');
expect(range[1][0]).toBeGreaterThan(180, 'east edge is unwrapped past 180');
},
null, BOXEVENTS, 'scattermap antimeridian select'
);
})
.then(function() {
return Plotly.relayout(gd, 'dragmode', 'lasso');
})
.then(function() {
return _run(false, lassoPath,
function() {
assertPoints(expectedPoints);
assertSelectedPoints({0: [0, 1, 2, 3, 4, 5]});

var lassoPoints = selectedData.lassoPoints.map;
for(var i = 1; i < lassoPoints.length; i++) {
expect(Math.abs(lassoPoints[i][0] - lassoPoints[i - 1][0]))
.toBeLessThan(180, 'continuous lasso longitude');
}
},
null, LASSOEVENTS, 'scattermap antimeridian lasso'
);
})
.then(done, done.fail);
}, LONG_TIMEOUT_INTERVAL);

[false, true].forEach(function(hasCssTransform) {
it('@gl should work on choroplethmap traces, hasCssTransform: ' + hasCssTransform, function(done) {
var assertPoints = makeAssertPoints(['location', 'z']);
Expand Down
Loading