-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathhover.js
116 lines (93 loc) · 3.58 KB
/
hover.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
'use strict';
var Fx = require('../../components/fx');
var Lib = require('../../lib');
var getTraceColor = require('../scatter/get_trace_color');
var fillText = Lib.fillText;
var BADNUM = require('../../constants/numerical').BADNUM;
var LAYER_PREFIX = require('../../plots/mapbox/constants').traceLayerPrefix;
function hoverPoints(pointData, xval, yval) {
var cd = pointData.cd;
var trace = cd[0].trace;
var xa = pointData.xa;
var ya = pointData.ya;
var subplot = pointData.subplot;
var clusteredPointsIds = [];
var layer = LAYER_PREFIX + trace.uid + '-circle';
var hasCluster = trace.cluster && trace.cluster.enabled;
if(hasCluster) {
var elems = subplot.map.queryRenderedFeatures(null, {layers: [layer]});
clusteredPointsIds = elems.map(function(elem) {return elem.id;});
}
// compute winding number about [-180, 180] globe
var winding = (xval >= 0) ?
Math.floor((xval + 180) / 360) :
Math.ceil((xval - 180) / 360);
// shift longitude to [-180, 180] to determine closest point
var lonShift = winding * 360;
var xval2 = xval - lonShift;
function distFn(d) {
var lonlat = d.lonlat;
if(lonlat[0] === BADNUM) return Infinity;
if(hasCluster && clusteredPointsIds.indexOf(d.i + 1) === -1) return Infinity;
var lon = Lib.modHalf(lonlat[0], 360);
var lat = lonlat[1];
var pt = subplot.project([lon, lat]);
var dx = pt.x - xa.c2p([xval2, lat]);
var dy = pt.y - ya.c2p([lon, yval]);
var rad = Math.max(3, d.mrc || 0);
return Math.max(Math.sqrt(dx * dx + dy * dy) - rad, 1 - 3 / rad);
}
Fx.getClosest(cd, distFn, pointData);
// skip the rest (for this trace) if we didn't find a close point
if(pointData.index === false) return;
var di = cd[pointData.index];
var lonlat = di.lonlat;
var lonlatShifted = [Lib.modHalf(lonlat[0], 360) + lonShift, lonlat[1]];
// shift labels back to original winded globe
var xc = xa.c2p(lonlatShifted);
var yc = ya.c2p(lonlatShifted);
var rad = di.mrc || 1;
pointData.x0 = xc - rad;
pointData.x1 = xc + rad;
pointData.y0 = yc - rad;
pointData.y1 = yc + rad;
var fullLayout = {};
fullLayout[trace.subplot] = {_subplot: subplot};
var labels = trace._module.formatLabels(di, trace, fullLayout);
pointData.lonLabel = labels.lonLabel;
pointData.latLabel = labels.latLabel;
pointData.color = getTraceColor(trace, di);
pointData.extraText = getExtraText(trace, di, cd[0].t.labels);
pointData.hovertemplate = trace.hovertemplate;
return [pointData];
}
function getExtraText(trace, di, labels) {
if(trace.hovertemplate) return;
var hoverinfo = di.hi || trace.hoverinfo;
var parts = hoverinfo.split('+');
var isAll = parts.indexOf('all') !== -1;
var hasLon = parts.indexOf('lon') !== -1;
var hasLat = parts.indexOf('lat') !== -1;
var lonlat = di.lonlat;
var text = [];
// TODO should we use a mock axis to format hover?
// If so, we'll need to make precision be zoom-level dependent
function format(v) {
return v + '\u00B0';
}
if(isAll || (hasLon && hasLat)) {
text.push('(' + format(lonlat[1]) + ', ' + format(lonlat[0]) + ')');
} else if(hasLon) {
text.push(labels.lon + format(lonlat[0]));
} else if(hasLat) {
text.push(labels.lat + format(lonlat[1]));
}
if(isAll || parts.indexOf('text') !== -1) {
fillText(di, trace, text);
}
return text.join('<br>');
}
module.exports = {
hoverPoints: hoverPoints,
getExtraText: getExtraText
};