blob: b34933d52f30970d8aadf5a36550d8e62ea1e9a1 [file] [log] [blame]
/*
title: Hexagonal Binning
category: custom, map
titleCN: 六边形分箱图(自定义系列)
difficulty: 6
*/
// Hexbin statistics code based on [d3-hexbin](https://github.com/d3/d3-hexbin)
function hexBinStatistics(points, r) {
var dx = r * 2 * Math.sin(Math.PI / 3);
var dy = r * 1.5;
var binsById = {};
var bins = [];
for (var i = 0, n = points.length; i < n; ++i) {
var point = points[i];
var px = point[0];
var py = point[1];
if (isNaN(px) || isNaN(py)) {
continue;
}
var pj = Math.round(py = py / dy);
var pi = Math.round(px = px / dx - (pj & 1) / 2);
var py1 = py - pj;
if (Math.abs(py1) * 3 > 1) {
var px1 = px - pi;
var pi2 = pi + (px < pi ? -1 : 1) / 2;
var pj2 = pj + (py < pj ? -1 : 1);
var px2 = px - pi2;
var py2 = py - pj2;
if (px1 * px1 + py1 * py1 > px2 * px2 + py2 * py2) {
pi = pi2 + (pj & 1 ? 1 : -1) / 2;
pj = pj2;
}
}
var id = pi + '-' + pj;
var bin = binsById[id];
if (bin) {
bin.points.push(point);
}
else {
bins.push(bin = binsById[id] = {points: [point]});
bin.x = (pi + (pj & 1) / 2) * dx;
bin.y = pj * dy;
}
}
var maxBinLen = -Infinity;
for (var i = 0; i < bins.length; i++) {
maxBinLen = Math.max(maxBinLen, bins.length);
}
return {
maxBinLen: maxBinLen,
bins: bins
};
}
$.when(
$.getJSON(ROOT_PATH + '/data/asset/data/kawhi-leonard-16-17-regular.json'),
$.getJSON(ROOT_PATH + '/data/asset/data/nba-court.json')
).done(function (shotData, nbaCourt) {
shotData = shotData[0];
nbaCourt = nbaCourt[0];
echarts.registerMap('nbaCourt', nbaCourt.borderGeoJSON);
var backgroundColor = '#333';
var hexagonRadiusInGeo = 1;
var hexBinResult = hexBinStatistics(
shotData.data.map(function (item) {
// "shot_made_flag" made missed
var made = item[shotData.schema.indexOf('shot_made_flag')];
return [
item[shotData.schema.indexOf('loc_x')],
item[shotData.schema.indexOf('loc_y')],
made === 'made' ? 1 : 0
];
}),
hexagonRadiusInGeo
);
var data = hexBinResult.bins.map(function (bin) {
var made = 0;
bin.points.forEach(function (point) {
made += point[2];
});
return [bin.x, bin.y, bin.points.length, (made / bin.points.length * 100).toFixed(2)];
});
function renderItemHexBin(params, api) {
var center = api.coord([api.value(0), api.value(1)]);
var points = [];
var pointsBG = [];
var maxViewRadius = api.size([hexagonRadiusInGeo, 0])[0];
var minViewRadius = Math.min(maxViewRadius, 4);
var extentMax = Math.log(Math.sqrt(hexBinResult.maxBinLen));
var viewRadius = echarts.number.linearMap(
Math.log(Math.sqrt(api.value(2))),
[0, extentMax],
[minViewRadius, maxViewRadius]
);
var angle = Math.PI / 6;
for (var i = 0; i < 6; i++, angle += Math.PI / 3) {
points.push([
center[0] + viewRadius * Math.cos(angle),
center[1] + viewRadius * Math.sin(angle)
]);
pointsBG.push([
center[0] + maxViewRadius * Math.cos(angle),
center[1] + maxViewRadius * Math.sin(angle)
]);
}
return {
type: 'group',
children: [{
type: 'polygon',
shape: {
points: points
},
style: {
stroke: '#ccc',
fill: api.visual('color'),
lineWidth: 1
}
}, {
type: 'polygon',
shape: {
points: pointsBG
},
style: {
stroke: null,
fill: 'rgba(0,0,0,0.5)',
lineWidth: 0
},
z2: -19
}]
};
}
function renderItemNBACourt(param, api) {
return {
type: 'group',
children: nbaCourt.geometry.map(function (item) {
return {
type: item.type,
style: {
stroke: '#aaa',
fill: null,
lineWidth: 1.5
},
shape: {
points: item.points.map(api.coord)
}
};
})
};
}
option = {
backgroundColor: backgroundColor,
tooltip: {
backgroundColor: 'rgba(255,255,255,0.9)',
textStyle: {
color: '#333'
}
},
animation: false,
title: {
text: 'Some Player',
subtext: 'Regular Season',
backgroundColor: backgroundColor,
top: 10,
left: 10,
textStyle: {
color: '#eee'
}
},
legend: {
data: ['bar', 'error']
},
geo: {
left: 0,
right: 0,
top: 0,
bottom: 0,
roam: true,
silent: true,
itemStyle: {
color: backgroundColor,
borderWidth: 0
},
map: 'nbaCourt'
},
visualMap: {
type: 'continuous',
orient: 'horizontal',
right: 30,
top: 40,
min: 0,
max: 100,
align: 'bottom',
text: [null, 'FG: '],
dimension: 3,
calculable: true,
textStyle: {
color: '#eee'
},
formatter: '{value} %',
inRange: {
// color: ['rgba(241,222,158, 0.3)', 'rgba(241,222,158, 1)']
color: ['green', 'yellow']
}
},
series: [{
type: 'custom',
coordinateSystem: 'geo',
geoIndex: 0,
renderItem: renderItemHexBin,
dimensions: [null, null, 'Field Goals Attempted (hexagon size)', 'Field Goal Percentage (color)'],
encode: {
tooltip: [2, 3]
},
data: data
}, {
coordinateSystem: 'geo',
type: 'custom',
geoIndex: 0,
renderItem: renderItemNBACourt,
silent: true,
data: [0]
}]
};
myChart.setOption(option);
// var width = 700;
// testHelper.createChart(echarts, 'hexagonal-binning', option, {
// width: width,
// height: width * nbaCourt.height / nbaCourt.width
// });
});