Skip to content

Commit 2d8348e

Browse files
committed
adapt choropleth plot and hover (in preparation for selections)
- make choropleth trace go though a calc step - use lib/polygon on hover 🎉, instead of old ad hoc mouse-event-on-svg-node way) - use clean enter/exit/merge d3 pattern instead of inner node purgery - bind 'real' calcdata items to selections, instead of patched geojson objects.
1 parent d2373a8 commit 2d8348e

File tree

3 files changed

+98
-81
lines changed

3 files changed

+98
-81
lines changed

src/traces/choropleth/calc.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,27 @@
99

1010
'use strict';
1111

12-
var colorscaleCalc = require('../../components/colorscale/calc');
12+
var isNumeric = require('fast-isnumeric');
13+
var BADNUM = require('../../constants/numerical').BADNUM;
1314

15+
var colorscaleCalc = require('../../components/colorscale/calc');
16+
var arraysToCalcdata = require('../scatter/arrays_to_calcdata');
1417

1518
module.exports = function calc(gd, trace) {
19+
var len = trace.locations.length;
20+
var calcTrace = new Array(len);
21+
22+
for(var i = 0; i < len; i++) {
23+
var calcPt = calcTrace[i] = {};
24+
var loc = trace.locations[i];
25+
var z = trace.z[i];
26+
27+
calcPt.loc = typeof loc === 'string' ? loc : null;
28+
calcPt.z = isNumeric(z) ? z : BADNUM;
29+
}
30+
31+
arraysToCalcdata(calcTrace, trace);
1632
colorscaleCalc(trace, trace.z, '', 'z');
33+
34+
return calcTrace;
1735
};

src/traces/choropleth/hover.js

+24-11
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,35 @@
1212
var Axes = require('../../plots/cartesian/axes');
1313
var attributes = require('./attributes');
1414

15-
module.exports = function hoverPoints(pointData) {
15+
module.exports = function hoverPoints(pointData, xval, yval) {
1616
var cd = pointData.cd;
1717
var trace = cd[0].trace;
1818
var geo = pointData.subplot;
1919

20-
// set on choropleth paths 'mouseover'
21-
var pt = geo.choroplethHoverPt;
20+
var pt, i, j, isInside;
2221

23-
if(!pt) return;
22+
for(i = 0; i < cd.length; i++) {
23+
pt = cd[i];
24+
isInside = false;
2425

25-
var centroid = geo.projection(pt.properties.ct);
26+
if(pt._polygons) {
27+
for(j = 0; j < pt._polygons.length; j++) {
28+
if(pt._polygons[j].contains([xval, yval])) {
29+
isInside = !isInside;
30+
}
31+
}
2632

27-
pointData.x0 = pointData.x1 = centroid[0];
28-
pointData.y0 = pointData.y1 = centroid[1];
33+
if(isInside) break;
34+
}
35+
}
36+
37+
if(!isInside || !pt) return;
38+
39+
pointData.x0 = pointData.x1 = pointData.xa.c2p(pt.ct);
40+
pointData.y0 = pointData.y1 = pointData.ya.c2p(pt.ct);
2941

3042
pointData.index = pt.index;
31-
pointData.location = pt.id;
43+
pointData.location = pt.loc;
3244
pointData.z = pt.z;
3345

3446
makeHoverInfo(pointData, trace, pt, geo.mockAxis);
@@ -55,10 +67,11 @@ function makeHoverInfo(pointData, trace, pt, axis) {
5567
return Axes.tickText(axis, axis.c2l(val), 'hover').text;
5668
}
5769

58-
if(hasIdAsNameLabel) pointData.nameOverride = pt.id;
59-
else {
70+
if(hasIdAsNameLabel) {
71+
pointData.nameOverride = pt.loc;
72+
} else {
6073
if(hasName) pointData.nameOverride = trace.name;
61-
if(hasLocation) text.push(pt.id);
74+
if(hasLocation) text.push(pt.loc);
6275
}
6376

6477
if(hasZ) text.push(formatter(pt.z));

src/traces/choropleth/plot.js

+55-69
Original file line numberDiff line numberDiff line change
@@ -11,100 +11,84 @@
1111

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

14+
var Lib = require('../../lib');
1415
var Color = require('../../components/color');
1516
var Drawing = require('../../components/drawing');
1617
var Colorscale = require('../../components/colorscale');
18+
var polygon = require('../../lib/polygon');
1719

1820
var getTopojsonFeatures = require('../../lib/topojson_utils').getTopojsonFeatures;
1921
var locationToFeature = require('../../lib/geo_location_utils').locationToFeature;
20-
var arrayToCalcItem = require('../../lib/array_to_calc_item');
2122

22-
var constants = require('../../plots/geo/constants');
23-
24-
module.exports = function plot(geo, calcData, geoLayout) {
23+
module.exports = function plot(geo, calcData) {
24+
for(var i = 0; i < calcData.length; i++) {
25+
calcGeoJSON(calcData[i], geo.topojson);
26+
}
2527

2628
function keyFunc(d) { return d[0].trace.uid; }
2729

28-
var framework = geo.framework,
29-
gChoropleth = framework.select('g.choroplethlayer'),
30-
gBaseLayer = framework.select('g.baselayer'),
31-
gBaseLayerOverChoropleth = framework.select('g.baselayeroverchoropleth'),
32-
baseLayersOverChoropleth = constants.baseLayersOverChoropleth,
33-
layerName;
34-
35-
var gChoroplethTraces = gChoropleth
30+
var gTraces = geo.layers.backplot.select('.choroplethlayer')
3631
.selectAll('g.trace.choropleth')
3732
.data(calcData, keyFunc);
3833

39-
gChoroplethTraces.enter().append('g')
34+
gTraces.enter().append('g')
4035
.attr('class', 'trace choropleth');
4136

42-
gChoroplethTraces.exit().remove();
37+
gTraces.exit().remove();
4338

44-
gChoroplethTraces.each(function(calcTrace) {
45-
var trace = calcTrace[0].trace,
46-
cdi = calcGeoJSON(trace, geo.topojson);
39+
gTraces.each(function(calcTrace) {
40+
var sel = calcTrace[0].node3 = d3.select(this);
4741

48-
var paths = d3.select(this)
49-
.selectAll('path.choroplethlocation')
50-
.data(cdi);
42+
var paths = sel.selectAll('path.choroplethlocation')
43+
.data(Lib.identity);
5144

5245
paths.enter().append('path')
53-
.classed('choroplethlocation', true)
54-
.on('mouseover', function(pt) {
55-
geo.choroplethHoverPt = pt;
56-
})
57-
.on('mouseout', function() {
58-
geo.choroplethHoverPt = null;
59-
});
46+
.classed('choroplethlocation', true);
6047

6148
paths.exit().remove();
6249
});
6350

64-
// some baselayers are drawn over choropleth
65-
gBaseLayerOverChoropleth.selectAll('*').remove();
66-
67-
for(var i = 0; i < baseLayersOverChoropleth.length; i++) {
68-
layerName = baseLayersOverChoropleth[i];
69-
gBaseLayer.select('g.' + layerName).remove();
70-
geo.drawTopo(gBaseLayerOverChoropleth, layerName, geoLayout);
71-
geo.styleLayer(gBaseLayerOverChoropleth, layerName, geoLayout);
72-
}
73-
7451
style(geo);
7552
};
7653

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 || {};
83-
84-
var feature;
85-
86-
for(var i = 0; i < len; i++) {
87-
feature = locationToFeature(trace.locationmode, locations[i], features);
88-
89-
if(!feature) continue; // filter the blank features here
90-
91-
// 'data_array' attributes
92-
feature.z = trace.z[i];
93-
if(trace.text !== undefined) feature.tx = trace.text[i];
94-
95-
// 'arrayOk' attributes
96-
arrayToCalcItem(markerLine.color, feature, 'mlc', i);
97-
arrayToCalcItem(markerLine.width, feature, 'mlw', i);
98-
99-
// for event data
100-
feature.index = i;
101-
102-
cdi.push(feature);
54+
function calcGeoJSON(calcTrace, topojson) {
55+
var trace = calcTrace[0].trace;
56+
var len = calcTrace.length;
57+
var features = getTopojsonFeatures(trace, topojson);
58+
var i, j, k;
59+
60+
for(i = 0; i < len; i++) {
61+
var calcPt = calcTrace[i];
62+
var feature = locationToFeature(trace.locationmode, calcPt.loc, features);
63+
64+
if(!feature) {
65+
calcPt.geojson = null;
66+
continue;
67+
}
68+
69+
calcPt.geojson = feature;
70+
calcPt.ct = feature.properties.ct;
71+
calcPt.index = i;
72+
73+
var geometry = feature.geometry;
74+
var coords = geometry.coordinates;
75+
calcPt._polygons = [];
76+
77+
switch(geometry.type) {
78+
case 'MultiPolygon':
79+
for(j = 0; j < coords.length; j++) {
80+
for(k = 0; k < coords[j].length; k++) {
81+
calcPt._polygons.push(polygon.tester(coords[j][k]));
82+
}
83+
}
84+
break;
85+
case 'Polygon':
86+
for(j = 0; j < coords.length; j++) {
87+
calcPt._polygons.push(polygon.tester(coords[j]));
88+
}
89+
break;
90+
}
10391
}
104-
105-
if(cdi.length > 0) cdi[0].trace = trace;
106-
107-
return cdi;
10892
}
10993

11094
function style(geo) {
@@ -122,9 +106,11 @@ function style(geo) {
122106
)
123107
);
124108

125-
s.selectAll('path.choroplethlocation').each(function(pt) {
109+
s.selectAll('path.choroplethlocation').each(function(_, i) {
110+
var pt = calcTrace[i];
111+
126112
d3.select(this)
127-
.attr('fill', function(pt) { return sclFunc(pt.z); })
113+
.attr('fill', sclFunc(pt.z))
128114
.call(Color.stroke, pt.mlc || markerLine.color)
129115
.call(Drawing.dashLine, '', pt.mlw || markerLine.width || 0);
130116
});

0 commit comments

Comments
 (0)