Skip to content

Commit 8c29dfb

Browse files
committed
refactor Choropleth plot
- only export one method (like all other trace plot method) - add hoverPoints and eventData methods making choropleth hover user Fx.hover logic (just like scattergeo) - set 'choroplethHoverPt' field on geo instance on mouseover choropleth location pt, use this to make hover info in hoverPoints
1 parent ec6c1b1 commit 8c29dfb

File tree

5 files changed

+153
-155
lines changed

5 files changed

+153
-155
lines changed

src/traces/choropleth/event_data.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Copyright 2012-2017, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
10+
'use strict';
11+
12+
module.exports = function eventData(out, pt) {
13+
out.location = pt.location;
14+
out.z = pt.z;
15+
16+
return out;
17+
};

src/traces/choropleth/hover.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Copyright 2012-2017, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
10+
'use strict';
11+
12+
var Axes = require('../../plots/cartesian/axes');
13+
var attributes = require('./attributes');
14+
15+
module.exports = function hoverPoints(pointData) {
16+
var cd = pointData.cd;
17+
var trace = cd[0].trace;
18+
var geo = pointData.subplot;
19+
20+
// set on choropleth paths 'mouseover'
21+
var pt = geo.choroplethHoverPt;
22+
23+
if(!pt) return;
24+
25+
var centroid = geo.projection(pt.properties.ct);
26+
27+
pointData.x0 = pointData.x1 = centroid[0];
28+
pointData.y0 = pointData.y1 = centroid[1];
29+
30+
pointData.index = pt.index;
31+
pointData.location = pt.id;
32+
pointData.z = pt.z;
33+
34+
makeHoverInfo(pointData, trace, pt, geo.mockAxis);
35+
36+
return [pointData];
37+
};
38+
39+
function makeHoverInfo(pointData, trace, pt, axis) {
40+
var hoverinfo = trace.hoverinfo;
41+
42+
var parts = (hoverinfo === 'all') ?
43+
attributes.hoverinfo.flags :
44+
hoverinfo.split('+');
45+
46+
var hasName = (parts.indexOf('name') !== -1),
47+
hasLocation = (parts.indexOf('location') !== -1),
48+
hasZ = (parts.indexOf('z') !== -1),
49+
hasText = (parts.indexOf('text') !== -1),
50+
hasIdAsNameLabel = !hasName && hasLocation;
51+
52+
var text = [];
53+
54+
function formatter(val) {
55+
return Axes.tickText(axis, axis.c2l(val), 'hover').text;
56+
}
57+
58+
if(hasIdAsNameLabel) pointData.nameOverride = pt.id;
59+
else {
60+
if(hasName) pointData.nameOverride = trace.name;
61+
if(hasLocation) text.push(pt.id);
62+
}
63+
64+
if(hasZ) text.push(formatter(pt.z));
65+
if(hasText) text.push(pt.tx);
66+
67+
pointData.extraText = text.join('<br>');
68+
}

src/traces/choropleth/index.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ Choropleth.attributes = require('./attributes');
1515
Choropleth.supplyDefaults = require('./defaults');
1616
Choropleth.colorbar = require('../heatmap/colorbar');
1717
Choropleth.calc = require('./calc');
18-
Choropleth.plot = require('./plot').plot;
19-
20-
// add dummy hover handler to skip Fx.hover w/o warnings
21-
Choropleth.hoverPoints = function() {};
18+
Choropleth.plot = require('./plot');
19+
Choropleth.hoverPoints = require('./hover');
20+
Choropleth.eventData = require('./event_data');
2221

2322
Choropleth.moduleType = 'trace';
2423
Choropleth.name = 'choropleth';

src/traces/choropleth/plot.js

Lines changed: 55 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
var d3 = require('d3');
1313

14-
var Axes = require('../../plots/cartesian/axes');
15-
var Fx = require('../../plots/cartesian/graph_interact');
1614
var Color = require('../../components/color');
1715
var Drawing = require('../../components/drawing');
1816
var Colorscale = require('../../components/colorscale');
@@ -22,42 +20,8 @@ var locationToFeature = require('../../lib/geo_location_utils').locationToFeatur
2220
var arrayToCalcItem = require('../../lib/array_to_calc_item');
2321

2422
var constants = require('../../plots/geo/constants');
25-
var attributes = require('./attributes');
2623

27-
var plotChoropleth = module.exports = {};
28-
29-
30-
plotChoropleth.calcGeoJSON = function(trace, topojson) {
31-
var cdi = [],
32-
locations = trace.locations,
33-
len = locations.length,
34-
features = getTopojsonFeatures(trace, topojson),
35-
markerLine = (trace.marker || {}).line || {};
36-
37-
var feature;
38-
39-
for(var i = 0; i < len; i++) {
40-
feature = locationToFeature(trace.locationmode, locations[i], features);
41-
42-
if(!feature) continue; // filter the blank features here
43-
44-
// 'data_array' attributes
45-
feature.z = trace.z[i];
46-
if(trace.text !== undefined) feature.tx = trace.text[i];
47-
48-
// 'arrayOk' attributes
49-
arrayToCalcItem(markerLine.color, feature, 'mlc', i);
50-
arrayToCalcItem(markerLine.width, feature, 'mlw', i);
51-
52-
cdi.push(feature);
53-
}
54-
55-
if(cdi.length > 0) cdi[0].trace = trace;
56-
57-
return cdi;
58-
};
59-
60-
plotChoropleth.plot = function(geo, calcData, geoLayout) {
24+
module.exports = function plot(geo, calcData, geoLayout) {
6125

6226
function keyFunc(d) { return d[0].trace.uid; }
6327

@@ -79,54 +43,20 @@ plotChoropleth.plot = function(geo, calcData, geoLayout) {
7943

8044
gChoroplethTraces.each(function(calcTrace) {
8145
var trace = calcTrace[0].trace,
82-
cdi = plotChoropleth.calcGeoJSON(trace, geo.topojson),
83-
cleanHoverLabelsFunc = makeCleanHoverLabelsFunc(geo, trace),
84-
eventDataFunc = makeEventDataFunc(trace);
85-
86-
// keep ref to event data in this scope for plotly_unhover
87-
var eventData = null;
88-
89-
function handleMouseOver(pt, ptIndex) {
90-
if(!geo.showHover) return;
91-
92-
var xy = geo.projection(pt.properties.ct);
93-
cleanHoverLabelsFunc(pt);
94-
95-
Fx.loneHover({
96-
x: xy[0],
97-
y: xy[1],
98-
name: pt.nameLabel,
99-
text: pt.textLabel
100-
}, {
101-
container: geo.hoverContainer.node()
102-
});
103-
104-
eventData = eventDataFunc(pt, ptIndex);
46+
cdi = calcGeoJSON(trace, geo.topojson);
10547

106-
geo.graphDiv.emit('plotly_hover', eventData);
107-
}
108-
109-
function handleClick(pt, ptIndex) {
110-
geo.graphDiv.emit('plotly_click', eventDataFunc(pt, ptIndex));
111-
}
112-
113-
var paths = d3.select(this).selectAll('path.choroplethlocation')
114-
.data(cdi);
48+
var paths = d3.select(this)
49+
.selectAll('path.choroplethlocation')
50+
.data(cdi);
11551

11652
paths.enter().append('path')
11753
.classed('choroplethlocation', true)
118-
.on('mouseover', handleMouseOver)
119-
.on('click', handleClick)
120-
.on('mouseout', function() {
121-
Fx.loneUnhover(geo.hoverContainer);
122-
123-
geo.graphDiv.emit('plotly_unhover', eventData);
124-
})
125-
.on('mousedown', function() {
126-
// to simulate the 'zoomon' event
127-
Fx.loneUnhover(geo.hoverContainer);
54+
.on('mouseover', function(pt) {
55+
geo.choroplethHoverPt = pt;
12856
})
129-
.on('mouseup', handleMouseOver); // ~ 'zoomend'
57+
.on('mouseout', function() {
58+
geo.choroplethHoverPt = null;
59+
});
13060

13161
paths.exit().remove();
13262
});
@@ -141,87 +71,62 @@ plotChoropleth.plot = function(geo, calcData, geoLayout) {
14171
geo.styleLayer(gBaseLayerOverChoropleth, layerName, geoLayout);
14272
}
14373

144-
plotChoropleth.style(geo);
74+
style(geo);
14575
};
14676

147-
plotChoropleth.style = function(geo) {
148-
geo.framework.selectAll('g.trace.choropleth')
149-
.each(function(calcTrace) {
150-
var trace = calcTrace[0].trace,
151-
s = d3.select(this),
152-
marker = trace.marker || {},
153-
markerLine = marker.line || {};
154-
155-
var sclFunc = Colorscale.makeColorScaleFunc(
156-
Colorscale.extractScale(
157-
trace.colorscale,
158-
trace.zmin,
159-
trace.zmax
160-
)
161-
);
162-
163-
s.selectAll('path.choroplethlocation')
164-
.each(function(pt) {
165-
d3.select(this)
166-
.attr('fill', function(pt) { return sclFunc(pt.z); })
167-
.call(Color.stroke, pt.mlc || markerLine.color)
168-
.call(Drawing.dashLine, '', pt.mlw || markerLine.width || 0);
169-
});
170-
});
171-
};
77+
function calcGeoJSON(trace, topojson) {
78+
var cdi = [],
79+
locations = trace.locations,
80+
len = locations.length,
81+
features = getTopojsonFeatures(trace, topojson),
82+
markerLine = (trace.marker || {}).line || {};
17283

173-
function makeCleanHoverLabelsFunc(geo, trace) {
174-
var hoverinfo = trace.hoverinfo;
84+
var feature;
17585

176-
if(hoverinfo === 'none' || hoverinfo === 'skip') {
177-
return function cleanHoverLabelsFunc(pt) {
178-
delete pt.nameLabel;
179-
delete pt.textLabel;
180-
};
181-
}
86+
for(var i = 0; i < len; i++) {
87+
feature = locationToFeature(trace.locationmode, locations[i], features);
18288

183-
var hoverinfoParts = (hoverinfo === 'all') ?
184-
attributes.hoverinfo.flags :
185-
hoverinfo.split('+');
89+
if(!feature) continue; // filter the blank features here
18690

187-
var hasName = (hoverinfoParts.indexOf('name') !== -1),
188-
hasLocation = (hoverinfoParts.indexOf('location') !== -1),
189-
hasZ = (hoverinfoParts.indexOf('z') !== -1),
190-
hasText = (hoverinfoParts.indexOf('text') !== -1),
191-
hasIdAsNameLabel = !hasName && hasLocation;
91+
// 'data_array' attributes
92+
feature.z = trace.z[i];
93+
if(trace.text !== undefined) feature.tx = trace.text[i];
19294

193-
function formatter(val) {
194-
var axis = geo.mockAxis;
195-
return Axes.tickText(axis, axis.c2l(val), 'hover').text;
196-
}
95+
// 'arrayOk' attributes
96+
arrayToCalcItem(markerLine.color, feature, 'mlc', i);
97+
arrayToCalcItem(markerLine.width, feature, 'mlw', i);
19798

198-
return function cleanHoverLabelsFunc(pt) {
199-
// put location id in name label container
200-
// if name isn't part of hoverinfo
201-
var thisText = [];
99+
// for event data
100+
feature.index = i;
202101

203-
if(hasIdAsNameLabel) pt.nameLabel = pt.id;
204-
else {
205-
if(hasName) pt.nameLabel = trace.name;
206-
if(hasLocation) thisText.push(pt.id);
207-
}
102+
cdi.push(feature);
103+
}
208104

209-
if(hasZ) thisText.push(formatter(pt.z));
210-
if(hasText) thisText.push(pt.tx);
105+
if(cdi.length > 0) cdi[0].trace = trace;
211106

212-
pt.textLabel = thisText.join('<br>');
213-
};
107+
return cdi;
214108
}
215109

216-
function makeEventDataFunc(trace) {
217-
return function(pt, ptIndex) {
218-
return {points: [{
219-
data: trace._input,
220-
fullData: trace,
221-
curveNumber: trace.index,
222-
pointNumber: ptIndex,
223-
location: pt.id,
224-
z: pt.z
225-
}]};
226-
};
110+
function style(geo) {
111+
geo.framework.selectAll('g.trace.choropleth').each(function(calcTrace) {
112+
var trace = calcTrace[0].trace,
113+
s = d3.select(this),
114+
marker = trace.marker || {},
115+
markerLine = marker.line || {};
116+
117+
var sclFunc = Colorscale.makeColorScaleFunc(
118+
Colorscale.extractScale(
119+
trace.colorscale,
120+
trace.zmin,
121+
trace.zmax
122+
)
123+
);
124+
125+
s.selectAll('path.choroplethlocation').each(function(pt) {
126+
d3.select(this)
127+
.attr('fill', function(pt) { return sclFunc(pt.z); })
128+
.call(Color.stroke, pt.mlc || markerLine.color)
129+
.call(Drawing.dashLine, '', pt.mlw || markerLine.width || 0);
130+
});
131+
});
227132
}

test/jasmine/tests/geo_test.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ describe('Test geo interactions', function() {
596596
describe('choropleth hover labels', function() {
597597
beforeEach(function() {
598598
mouseEventChoropleth('mouseover');
599+
mouseEventChoropleth('mousemove');
599600
});
600601

601602
it('should show one hover text group', function() {
@@ -625,6 +626,7 @@ describe('Test geo interactions', function() {
625626
});
626627

627628
mouseEventChoropleth('mouseover');
629+
mouseEventChoropleth('mousemove');
628630
});
629631

630632
it('should contain the correct fields', function() {
@@ -650,6 +652,8 @@ describe('Test geo interactions', function() {
650652
ptData = eventData.points[0];
651653
});
652654

655+
mouseEventChoropleth('mouseover');
656+
mouseEventChoropleth('mousemove');
653657
mouseEventChoropleth('click');
654658
});
655659

@@ -671,13 +675,18 @@ describe('Test geo interactions', function() {
671675
describe('choropleth unhover events', function() {
672676
var ptData;
673677

674-
beforeEach(function() {
678+
beforeEach(function(done) {
675679
gd.on('plotly_unhover', function(eventData) {
676680
ptData = eventData.points[0];
677681
});
678682

679683
mouseEventChoropleth('mouseover');
684+
mouseEventChoropleth('mousemove');
680685
mouseEventChoropleth('mouseout');
686+
setTimeout(function() {
687+
mouseEvent('mousemove', 300, 235);
688+
done();
689+
}, HOVERMINTIME + 100);
681690
});
682691

683692
it('should contain the correct fields', function() {

0 commit comments

Comments
 (0)