Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c58fe87

Browse files
committed
Merge pull request plotly#215 from plotly/geo-events
Scattergeo and choropleth click and hover events
2 parents f7fee8f + c384cf4 commit c58fe87

File tree

5 files changed

+224
-64
lines changed

5 files changed

+224
-64
lines changed

src/plots/geo/geo.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,15 @@ var topojsonFeature = require('topojson').feature;
3333
function Geo(options, fullLayout) {
3434

3535
this.id = options.id;
36+
this.graphDiv = options.graphDiv;
3637
this.container = options.container;
3738
this.topojsonURL = options.topojsonURL;
3839

3940
// add a few projection types to d3.geo,
4041
// a subset of https://github.com/d3/d3-geo-projection
4142
addProjectionsToD3();
4243

43-
this.showHover = fullLayout.hovermode==='closest';
44+
this.showHover = (fullLayout.hovermode === 'closest');
4445
this.hoverContainer = null;
4546

4647
this.topojsonName = null;

src/plots/geo/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ exports.plot = function plotGeo(gd) {
5252
if(geo === undefined) {
5353
geo = new Geo({
5454
id: geoId,
55+
graphDiv: gd,
5556
container: fullLayout._geocontainer.node(),
5657
topojsonURL: gd._context.topojsonURL
5758
},

src/traces/choropleth/plot.js

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -59,46 +59,49 @@ plotChoropleth.calcGeoJSON = function(trace, topojson) {
5959

6060
plotChoropleth.plot = function(geo, choroplethData, geoLayout) {
6161
var framework = geo.framework,
62-
topojson = geo.topojson,
6362
gChoropleth = framework.select('g.choroplethlayer'),
6463
gBaseLayer = framework.select('g.baselayer'),
6564
gBaseLayerOverChoropleth = framework.select('g.baselayeroverchoropleth'),
6665
baseLayersOverChoropleth = constants.baseLayersOverChoropleth,
6766
layerName;
6867

69-
// TODO move to more d3-idiomatic pattern (that's work on replot)
70-
// N.B. html('') does not work in IE11
71-
gChoropleth.selectAll('*').remove();
72-
gBaseLayerOverChoropleth.selectAll('*').remove();
73-
7468
var gChoroplethTraces = gChoropleth
75-
.selectAll('g.trace.scatter')
69+
.selectAll('g.trace.choropleth')
7670
.data(choroplethData);
7771

7872
gChoroplethTraces.enter().append('g')
79-
.attr('class', 'trace choropleth');
73+
.attr('class', 'trace choropleth');
74+
75+
gChoroplethTraces.exit().remove();
8076

8177
gChoroplethTraces
8278
.each(function(trace) {
8379
if(trace.visible !== true) return;
8480

85-
var cdi = plotChoropleth.calcGeoJSON(trace, topojson),
86-
cleanHoverLabelsFunc = makeCleanHoverLabelsFunc(geo, trace);
81+
var cdi = plotChoropleth.calcGeoJSON(trace, geo.topojson),
82+
cleanHoverLabelsFunc = makeCleanHoverLabelsFunc(geo, trace),
83+
eventDataFunc = makeEventDataFunc(trace);
8784

88-
function handleMouseOver(d) {
85+
function handleMouseOver(pt, ptIndex) {
8986
if(!geo.showHover) return;
9087

91-
var xy = geo.projection(d.properties.ct);
92-
cleanHoverLabelsFunc(d);
88+
var xy = geo.projection(pt.properties.ct);
89+
cleanHoverLabelsFunc(pt);
9390

9491
Plotly.Fx.loneHover({
9592
x: xy[0],
9693
y: xy[1],
97-
name: d.nameLabel,
98-
text: d.textLabel
94+
name: pt.nameLabel,
95+
text: pt.textLabel
9996
}, {
10097
container: geo.hoverContainer.node()
10198
});
99+
100+
geo.graphDiv.emit('plotly_hover', eventDataFunc(pt, ptIndex));
101+
}
102+
103+
function handleClick(pt, ptIndex) {
104+
geo.graphDiv.emit('plotly_click', eventDataFunc(pt, ptIndex));
102105
}
103106

104107
d3.select(this)
@@ -107,6 +110,7 @@ plotChoropleth.plot = function(geo, choroplethData, geoLayout) {
107110
.enter().append('path')
108111
.attr('class', 'choroplethlocation')
109112
.on('mouseover', handleMouseOver)
113+
.on('click', handleClick)
110114
.on('mouseout', function() {
111115
Plotly.Fx.loneUnhover(geo.hoverContainer);
112116
})
@@ -118,6 +122,8 @@ plotChoropleth.plot = function(geo, choroplethData, geoLayout) {
118122
});
119123

120124
// some baselayers are drawn over choropleth
125+
gBaseLayerOverChoropleth.selectAll('*').remove();
126+
121127
for(var i = 0; i < baseLayersOverChoropleth.length; i++) {
122128
layerName = baseLayersOverChoropleth[i];
123129
gBaseLayer.select('g.' + layerName).remove();
@@ -140,11 +146,11 @@ plotChoropleth.style = function(geo) {
140146
sclFunc = makeScaleFunction(scl, zmin, zmax);
141147

142148
s.selectAll('path.choroplethlocation')
143-
.each(function(d) {
149+
.each(function(pt) {
144150
d3.select(this)
145-
.attr('fill', function(d) { return sclFunc(d.z); })
146-
.call(Color.stroke, d.mlc || markerLine.color)
147-
.call(Drawing.dashLine, '', d.mlw || markerLine.width);
151+
.attr('fill', function(pt) { return sclFunc(pt.z); })
152+
.call(Color.stroke, pt.mlc || markerLine.color)
153+
.call(Drawing.dashLine, '', pt.mlw || markerLine.width);
148154
});
149155
});
150156
};
@@ -153,9 +159,9 @@ function makeCleanHoverLabelsFunc(geo, trace) {
153159
var hoverinfo = trace.hoverinfo;
154160

155161
if(hoverinfo === 'none') {
156-
return function cleanHoverLabelsFunc(d) {
157-
delete d.nameLabel;
158-
delete d.textLabel;
162+
return function cleanHoverLabelsFunc(pt) {
163+
delete pt.nameLabel;
164+
delete pt.textLabel;
159165
};
160166
}
161167

@@ -174,20 +180,33 @@ function makeCleanHoverLabelsFunc(geo, trace) {
174180
return Plotly.Axes.tickText(axis, axis.c2l(val), 'hover').text;
175181
}
176182

177-
return function cleanHoverLabelsFunc(d) {
183+
return function cleanHoverLabelsFunc(pt) {
178184
// put location id in name label container
179185
// if name isn't part of hoverinfo
180186
var thisText = [];
181187

182-
if(hasIdAsNameLabel) d.nameLabel = d.id;
188+
if(hasIdAsNameLabel) pt.nameLabel = pt.id;
183189
else {
184-
if(hasName) d.nameLabel = trace.name;
185-
if(hasLocation) thisText.push(d.id);
190+
if(hasName) pt.nameLabel = trace.name;
191+
if(hasLocation) thisText.push(pt.id);
186192
}
187193

188-
if(hasZ) thisText.push(formatter(d.z));
189-
if(hasText) thisText.push(d.tx);
194+
if(hasZ) thisText.push(formatter(pt.z));
195+
if(hasText) thisText.push(pt.tx);
196+
197+
pt.textLabel = thisText.join('<br>');
198+
};
199+
}
190200

191-
d.textLabel = thisText.join('<br>');
201+
function makeEventDataFunc(trace) {
202+
return function(pt, ptIndex) {
203+
return {points: [{
204+
data: trace._input,
205+
fullData: trace,
206+
curveNumber: trace.index,
207+
pointNumber: ptIndex,
208+
location: pt.id,
209+
z: pt.z
210+
}]};
192211
};
193212
}

src/traces/scattergeo/plot.js

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -116,19 +116,14 @@ function makeLineGeoJSON(trace) {
116116
}
117117

118118
plotScatterGeo.plot = function(geo, scattergeoData) {
119-
var gScatterGeo = geo.framework.select('g.scattergeolayer'),
120-
topojson = geo.topojson;
121-
122-
// TODO move to more d3-idiomatic pattern (that's work on replot)
123-
// N.B. html('') does not work in IE11
124-
gScatterGeo.selectAll('*').remove();
125-
126-
var gScatterGeoTraces = gScatterGeo
127-
.selectAll('g.trace.scatter')
119+
var gScatterGeoTraces = geo.framework.select('.scattergeolayer')
120+
.selectAll('g.trace.scattergeo')
128121
.data(scattergeoData);
129122

130123
gScatterGeoTraces.enter().append('g')
131-
.attr('class', 'trace scattergeo');
124+
.attr('class', 'trace scattergeo');
125+
126+
gScatterGeoTraces.exit().remove();
132127

133128
// TODO add hover - how?
134129
gScatterGeoTraces
@@ -152,28 +147,37 @@ plotScatterGeo.plot = function(geo, scattergeoData) {
152147
return;
153148
}
154149

155-
var cdi = plotScatterGeo.calcGeoJSON(trace, topojson),
156-
cleanHoverLabelsFunc = makeCleanHoverLabelsFunc(geo, trace);
150+
var cdi = plotScatterGeo.calcGeoJSON(trace, geo.topojson),
151+
cleanHoverLabelsFunc = makeCleanHoverLabelsFunc(geo, trace),
152+
eventDataFunc = makeEventDataFunc(trace);
157153

158154
var hoverinfo = trace.hoverinfo,
159-
hasNameLabel = (hoverinfo === 'all' ||
160-
hoverinfo.indexOf('name') !== -1);
155+
hasNameLabel = (
156+
hoverinfo === 'all' ||
157+
hoverinfo.indexOf('name') !== -1
158+
);
161159

162-
function handleMouseOver(d) {
160+
function handleMouseOver(pt, ptIndex) {
163161
if(!geo.showHover) return;
164162

165-
var xy = geo.projection([d.lon, d.lat]);
166-
cleanHoverLabelsFunc(d);
163+
var xy = geo.projection([pt.lon, pt.lat]);
164+
cleanHoverLabelsFunc(pt);
167165

168166
Fx.loneHover({
169167
x: xy[0],
170168
y: xy[1],
171169
name: hasNameLabel ? trace.name : undefined,
172-
text: d.textLabel,
173-
color: d.mc || (trace.marker || {}).color
170+
text: pt.textLabel,
171+
color: pt.mc || (trace.marker || {}).color
174172
}, {
175173
container: geo.hoverContainer.node()
176174
});
175+
176+
geo.graphDiv.emit('plotly_hover', eventDataFunc(pt, ptIndex));
177+
}
178+
179+
function handleClick(pt, ptIndex) {
180+
geo.graphDiv.emit('plotly_click', eventDataFunc(pt, ptIndex));
177181
}
178182

179183
if(showMarkers) {
@@ -182,6 +186,7 @@ plotScatterGeo.plot = function(geo, scattergeoData) {
182186
.enter().append('path')
183187
.attr('class', 'point')
184188
.on('mouseover', handleMouseOver)
189+
.on('click', handleClick)
185190
.on('mouseout', function() {
186191
Fx.loneUnhover(geo.hoverContainer);
187192
})
@@ -237,11 +242,13 @@ function makeCleanHoverLabelsFunc(geo, trace) {
237242
}
238243

239244
var hoverinfoParts = (hoverinfo === 'all') ?
240-
attributes.hoverinfo.flags :
241-
hoverinfo.split('+');
245+
attributes.hoverinfo.flags :
246+
hoverinfo.split('+');
242247

243-
var hasLocation = (hoverinfoParts.indexOf('location') !== -1 &&
244-
Array.isArray(trace.locations)),
248+
var hasLocation = (
249+
hoverinfoParts.indexOf('location') !== -1 &&
250+
Array.isArray(trace.locations)
251+
),
245252
hasLon = (hoverinfoParts.indexOf('lon') !== -1),
246253
hasLat = (hoverinfoParts.indexOf('lat') !== -1),
247254
hasText = (hoverinfoParts.indexOf('text') !== -1);
@@ -251,18 +258,34 @@ function makeCleanHoverLabelsFunc(geo, trace) {
251258
return Axes.tickText(axis, axis.c2l(val), 'hover').text + '\u00B0';
252259
}
253260

254-
return function cleanHoverLabelsFunc(d) {
261+
return function cleanHoverLabelsFunc(pt) {
255262
var thisText = [];
256263

257-
if(hasLocation) thisText.push(d.location);
264+
if(hasLocation) thisText.push(pt.location);
258265
else if(hasLon && hasLat) {
259-
thisText.push('(' + formatter(d.lon) + ', ' + formatter(d.lat) + ')');
266+
thisText.push('(' + formatter(pt.lon) + ', ' + formatter(pt.lat) + ')');
260267
}
261-
else if(hasLon) thisText.push('lon: ' + formatter(d.lon));
262-
else if(hasLat) thisText.push('lat: ' + formatter(d.lat));
268+
else if(hasLon) thisText.push('lon: ' + formatter(pt.lon));
269+
else if(hasLat) thisText.push('lat: ' + formatter(pt.lat));
270+
271+
if(hasText) thisText.push(pt.tx || trace.text);
263272

264-
if(hasText) thisText.push(d.tx || trace.text);
273+
pt.textLabel = thisText.join('<br>');
274+
};
275+
}
265276

266-
d.textLabel = thisText.join('<br>');
277+
function makeEventDataFunc(trace) {
278+
var hasLocation = Array.isArray(trace.locations);
279+
280+
return function(pt, ptIndex) {
281+
return {points: [{
282+
data: trace._input,
283+
fullData: trace,
284+
curveNumber: trace.index,
285+
pointNumber: ptIndex,
286+
lon: pt.lon,
287+
lat: pt.lat,
288+
location: hasLocation ? pt.location : null
289+
}]};
267290
};
268291
}

0 commit comments

Comments
 (0)