123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- <html>
- <head>
- <meta charset="utf-8">
- <script src="lib/esl.js"></script>
- <script src="lib/config.js"></script>
- <script src="lib/jquery.min.js"></script>
- <script src="lib/testHelper.js"></script>
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <link rel="stylesheet" href="lib/reset.css" />
- </head>
- <body>
- <style>
- h1 {
- line-height: 60px;
- height: 60px;
- background: #ddd;
- text-align: center;
- font-weight: bold;
- font-size: 14px;
- }
- .chart {
- height: 500px;
- margin: 10px auto;
- }
- </style>
- <h1>Hexagonal Binning</h1>
- <div class="chart" id="hexagonal-binning"></div>
- <script>
- // 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
- };
- }
- </script>
- <script>
- require([
- 'echarts'
- // 'echarts/chart/custom',
- // 'echarts/chart/bar',
- // 'echarts/component/title',
- // 'echarts/component/geo',
- // 'echarts/component/legend',
- // 'echarts/component/tooltip',
- // 'echarts/component/visualMap',
- // 'echarts/component/dataZoom',
- // 'zrender/vml/vml'
- ], function (echarts) {
- // 2006-2007 Regular Season
- $.getJSON('./data/kawhi-leonard-16-17-regular.json', function (shotData) {
- $.getJSON('./data/nba-court.json', function (nbaCourt) {
- echarts.registerMap('nbaCourt', nbaCourt.borderGeoJSON);
- var backgroundColor = '#333';
- var hexagonRadiusInGeo = 1;
- var hexBinResult = hexBinStatistics(
- echarts.util.map(shotData.data, function (item) {
- // "shot_made_flag" made missed
- var made = item[echarts.util.indexOf(shotData.schema, 'shot_made_flag')];
- return [
- item[echarts.util.indexOf(shotData.schema, 'loc_x')],
- item[echarts.util.indexOf(shotData.schema, 'loc_y')],
- made === 'made' ? 1 : 0
- ];
- }),
- hexagonRadiusInGeo
- );
- var data = echarts.util.map(hexBinResult.bins, function (bin) {
- var made = 0;
- echarts.util.each(bin.points, 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: 0
- }
- }, {
- 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: echarts.util.map(nbaCourt.geometry, function (item) {
- return {
- type: item.type,
- style: {
- stroke: '#aaa',
- fill: null,
- lineWidth: 1.5
- },
- shape: {
- points: echarts.util.map(item.points, api.coord)
- }
- };
- })
- };
- }
- var option = {
- backgroundColor: backgroundColor,
- aria: {
- show: true
- },
- tooltip: {
- backgroundColor: 'rgba(255,255,255,0.9)',
- textStyle: {
- color: '#333'
- }
- },
- title: {
- text: 'Kawhi Leonard',
- subtext: '2016-2017 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: {
- normal: {
- 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,
- seriesIndex: 0,
- 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]
- }]
- };
- var width = 700;
- testHelper.createChart(echarts, 'hexagonal-binning', option, {
- width: width,
- height: width * nbaCourt.height / nbaCourt.width
- });
- });
- });
- });
- </script>
- </body>
- </html>
|