-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Scattergeo and choropleth click and hover events #215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f32d425
e8ebcb2
f99c366
c384cf4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,46 +59,49 @@ plotChoropleth.calcGeoJSON = function(trace, topojson) { | |
|
||
plotChoropleth.plot = function(geo, choroplethData, geoLayout) { | ||
var framework = geo.framework, | ||
topojson = geo.topojson, | ||
gChoropleth = framework.select('g.choroplethlayer'), | ||
gBaseLayer = framework.select('g.baselayer'), | ||
gBaseLayerOverChoropleth = framework.select('g.baselayeroverchoropleth'), | ||
baseLayersOverChoropleth = constants.baseLayersOverChoropleth, | ||
layerName; | ||
|
||
// TODO move to more d3-idiomatic pattern (that's work on replot) | ||
// N.B. html('') does not work in IE11 | ||
gChoropleth.selectAll('*').remove(); | ||
gBaseLayerOverChoropleth.selectAll('*').remove(); | ||
|
||
var gChoroplethTraces = gChoropleth | ||
.selectAll('g.trace.scatter') | ||
.selectAll('g.trace.choropleth') | ||
.data(choroplethData); | ||
|
||
gChoroplethTraces.enter().append('g') | ||
.attr('class', 'trace choropleth'); | ||
.attr('class', 'trace choropleth'); | ||
|
||
gChoroplethTraces.exit().remove(); | ||
|
||
gChoroplethTraces | ||
.each(function(trace) { | ||
if(trace.visible !== true) return; | ||
|
||
var cdi = plotChoropleth.calcGeoJSON(trace, topojson), | ||
cleanHoverLabelsFunc = makeCleanHoverLabelsFunc(geo, trace); | ||
var cdi = plotChoropleth.calcGeoJSON(trace, geo.topojson), | ||
cleanHoverLabelsFunc = makeCleanHoverLabelsFunc(geo, trace), | ||
eventDataFunc = makeEventDataFunc(trace); | ||
|
||
function handleMouseOver(d) { | ||
function handleMouseOver(pt, ptIndex) { | ||
if(!geo.showHover) return; | ||
|
||
var xy = geo.projection(d.properties.ct); | ||
cleanHoverLabelsFunc(d); | ||
var xy = geo.projection(pt.properties.ct); | ||
cleanHoverLabelsFunc(pt); | ||
|
||
Plotly.Fx.loneHover({ | ||
x: xy[0], | ||
y: xy[1], | ||
name: d.nameLabel, | ||
text: d.textLabel | ||
name: pt.nameLabel, | ||
text: pt.textLabel | ||
}, { | ||
container: geo.hoverContainer.node() | ||
}); | ||
|
||
geo.graphDiv.emit('plotly_hover', eventDataFunc(pt, ptIndex)); | ||
} | ||
|
||
function handleClick(pt, ptIndex) { | ||
geo.graphDiv.emit('plotly_click', eventDataFunc(pt, ptIndex)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎉 |
||
} | ||
|
||
d3.select(this) | ||
|
@@ -107,6 +110,7 @@ plotChoropleth.plot = function(geo, choroplethData, geoLayout) { | |
.enter().append('path') | ||
.attr('class', 'choroplethlocation') | ||
.on('mouseover', handleMouseOver) | ||
.on('click', handleClick) | ||
.on('mouseout', function() { | ||
Plotly.Fx.loneUnhover(geo.hoverContainer); | ||
}) | ||
|
@@ -118,6 +122,8 @@ plotChoropleth.plot = function(geo, choroplethData, geoLayout) { | |
}); | ||
|
||
// some baselayers are drawn over choropleth | ||
gBaseLayerOverChoropleth.selectAll('*').remove(); | ||
|
||
for(var i = 0; i < baseLayersOverChoropleth.length; i++) { | ||
layerName = baseLayersOverChoropleth[i]; | ||
gBaseLayer.select('g.' + layerName).remove(); | ||
|
@@ -140,11 +146,11 @@ plotChoropleth.style = function(geo) { | |
sclFunc = makeScaleFunction(scl, zmin, zmax); | ||
|
||
s.selectAll('path.choroplethlocation') | ||
.each(function(d) { | ||
.each(function(pt) { | ||
d3.select(this) | ||
.attr('fill', function(d) { return sclFunc(d.z); }) | ||
.call(Color.stroke, d.mlc || markerLine.color) | ||
.call(Drawing.dashLine, '', d.mlw || markerLine.width); | ||
.attr('fill', function(pt) { return sclFunc(pt.z); }) | ||
.call(Color.stroke, pt.mlc || markerLine.color) | ||
.call(Drawing.dashLine, '', pt.mlw || markerLine.width); | ||
}); | ||
}); | ||
}; | ||
|
@@ -153,9 +159,9 @@ function makeCleanHoverLabelsFunc(geo, trace) { | |
var hoverinfo = trace.hoverinfo; | ||
|
||
if(hoverinfo === 'none') { | ||
return function cleanHoverLabelsFunc(d) { | ||
delete d.nameLabel; | ||
delete d.textLabel; | ||
return function cleanHoverLabelsFunc(pt) { | ||
delete pt.nameLabel; | ||
delete pt.textLabel; | ||
}; | ||
} | ||
|
||
|
@@ -174,20 +180,33 @@ function makeCleanHoverLabelsFunc(geo, trace) { | |
return Plotly.Axes.tickText(axis, axis.c2l(val), 'hover').text; | ||
} | ||
|
||
return function cleanHoverLabelsFunc(d) { | ||
return function cleanHoverLabelsFunc(pt) { | ||
// put location id in name label container | ||
// if name isn't part of hoverinfo | ||
var thisText = []; | ||
|
||
if(hasIdAsNameLabel) d.nameLabel = d.id; | ||
if(hasIdAsNameLabel) pt.nameLabel = pt.id; | ||
else { | ||
if(hasName) d.nameLabel = trace.name; | ||
if(hasLocation) thisText.push(d.id); | ||
if(hasName) pt.nameLabel = trace.name; | ||
if(hasLocation) thisText.push(pt.id); | ||
} | ||
|
||
if(hasZ) thisText.push(formatter(d.z)); | ||
if(hasText) thisText.push(d.tx); | ||
if(hasZ) thisText.push(formatter(pt.z)); | ||
if(hasText) thisText.push(pt.tx); | ||
|
||
pt.textLabel = thisText.join('<br>'); | ||
}; | ||
} | ||
|
||
d.textLabel = thisText.join('<br>'); | ||
function makeEventDataFunc(trace) { | ||
return function(pt, ptIndex) { | ||
return {points: [{ | ||
data: trace._input, | ||
fullData: trace, | ||
curveNumber: trace.index, | ||
pointNumber: ptIndex, | ||
location: pt.id, | ||
z: pt.z | ||
}]}; | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,19 +116,14 @@ function makeLineGeoJSON(trace) { | |
} | ||
|
||
plotScatterGeo.plot = function(geo, scattergeoData) { | ||
var gScatterGeo = geo.framework.select('g.scattergeolayer'), | ||
topojson = geo.topojson; | ||
|
||
// TODO move to more d3-idiomatic pattern (that's work on replot) | ||
// N.B. html('') does not work in IE11 | ||
gScatterGeo.selectAll('*').remove(); | ||
|
||
var gScatterGeoTraces = gScatterGeo | ||
.selectAll('g.trace.scatter') | ||
var gScatterGeoTraces = geo.framework.select('.scattergeolayer') | ||
.selectAll('g.trace.scattergeo') | ||
.data(scattergeoData); | ||
|
||
gScatterGeoTraces.enter().append('g') | ||
.attr('class', 'trace scattergeo'); | ||
.attr('class', 'trace scattergeo'); | ||
|
||
gScatterGeoTraces.exit().remove(); | ||
|
||
// TODO add hover - how? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't need this anymore 🎉 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh, we still need this unfortunately. scattergeo trace with mode |
||
gScatterGeoTraces | ||
|
@@ -152,28 +147,37 @@ plotScatterGeo.plot = function(geo, scattergeoData) { | |
return; | ||
} | ||
|
||
var cdi = plotScatterGeo.calcGeoJSON(trace, topojson), | ||
cleanHoverLabelsFunc = makeCleanHoverLabelsFunc(geo, trace); | ||
var cdi = plotScatterGeo.calcGeoJSON(trace, geo.topojson), | ||
cleanHoverLabelsFunc = makeCleanHoverLabelsFunc(geo, trace), | ||
eventDataFunc = makeEventDataFunc(trace); | ||
|
||
var hoverinfo = trace.hoverinfo, | ||
hasNameLabel = (hoverinfo === 'all' || | ||
hoverinfo.indexOf('name') !== -1); | ||
hasNameLabel = ( | ||
hoverinfo === 'all' || | ||
hoverinfo.indexOf('name') !== -1 | ||
); | ||
|
||
function handleMouseOver(d) { | ||
function handleMouseOver(pt, ptIndex) { | ||
if(!geo.showHover) return; | ||
|
||
var xy = geo.projection([d.lon, d.lat]); | ||
cleanHoverLabelsFunc(d); | ||
var xy = geo.projection([pt.lon, pt.lat]); | ||
cleanHoverLabelsFunc(pt); | ||
|
||
Fx.loneHover({ | ||
x: xy[0], | ||
y: xy[1], | ||
name: hasNameLabel ? trace.name : undefined, | ||
text: d.textLabel, | ||
color: d.mc || (trace.marker || {}).color | ||
text: pt.textLabel, | ||
color: pt.mc || (trace.marker || {}).color | ||
}, { | ||
container: geo.hoverContainer.node() | ||
}); | ||
|
||
geo.graphDiv.emit('plotly_hover', eventDataFunc(pt, ptIndex)); | ||
} | ||
|
||
function handleClick(pt, ptIndex) { | ||
geo.graphDiv.emit('plotly_click', eventDataFunc(pt, ptIndex)); | ||
} | ||
|
||
if(showMarkers) { | ||
|
@@ -182,6 +186,7 @@ plotScatterGeo.plot = function(geo, scattergeoData) { | |
.enter().append('path') | ||
.attr('class', 'point') | ||
.on('mouseover', handleMouseOver) | ||
.on('click', handleClick) | ||
.on('mouseout', function() { | ||
Fx.loneUnhover(geo.hoverContainer); | ||
}) | ||
|
@@ -237,11 +242,13 @@ function makeCleanHoverLabelsFunc(geo, trace) { | |
} | ||
|
||
var hoverinfoParts = (hoverinfo === 'all') ? | ||
attributes.hoverinfo.flags : | ||
hoverinfo.split('+'); | ||
attributes.hoverinfo.flags : | ||
hoverinfo.split('+'); | ||
|
||
var hasLocation = (hoverinfoParts.indexOf('location') !== -1 && | ||
Array.isArray(trace.locations)), | ||
var hasLocation = ( | ||
hoverinfoParts.indexOf('location') !== -1 && | ||
Array.isArray(trace.locations) | ||
), | ||
hasLon = (hoverinfoParts.indexOf('lon') !== -1), | ||
hasLat = (hoverinfoParts.indexOf('lat') !== -1), | ||
hasText = (hoverinfoParts.indexOf('text') !== -1); | ||
|
@@ -251,18 +258,34 @@ function makeCleanHoverLabelsFunc(geo, trace) { | |
return Axes.tickText(axis, axis.c2l(val), 'hover').text + '\u00B0'; | ||
} | ||
|
||
return function cleanHoverLabelsFunc(d) { | ||
return function cleanHoverLabelsFunc(pt) { | ||
var thisText = []; | ||
|
||
if(hasLocation) thisText.push(d.location); | ||
if(hasLocation) thisText.push(pt.location); | ||
else if(hasLon && hasLat) { | ||
thisText.push('(' + formatter(d.lon) + ', ' + formatter(d.lat) + ')'); | ||
thisText.push('(' + formatter(pt.lon) + ', ' + formatter(pt.lat) + ')'); | ||
} | ||
else if(hasLon) thisText.push('lon: ' + formatter(d.lon)); | ||
else if(hasLat) thisText.push('lat: ' + formatter(d.lat)); | ||
else if(hasLon) thisText.push('lon: ' + formatter(pt.lon)); | ||
else if(hasLat) thisText.push('lat: ' + formatter(pt.lat)); | ||
|
||
if(hasText) thisText.push(pt.tx || trace.text); | ||
|
||
if(hasText) thisText.push(d.tx || trace.text); | ||
pt.textLabel = thisText.join('<br>'); | ||
}; | ||
} | ||
|
||
d.textLabel = thisText.join('<br>'); | ||
function makeEventDataFunc(trace) { | ||
var hasLocation = Array.isArray(trace.locations); | ||
|
||
return function(pt, ptIndex) { | ||
return {points: [{ | ||
data: trace._input, | ||
fullData: trace, | ||
curveNumber: trace.index, | ||
pointNumber: ptIndex, | ||
lon: pt.lon, | ||
lat: pt.lat, | ||
location: hasLocation ? pt.location : null | ||
}]}; | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉