From 1b086936a927aed44e505b12239c78fefa1e058c Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Fri, 8 Mar 2019 20:51:11 +0100 Subject: chart! --- client/applet.js | 3 + client/chart/chart.container.js | 200 ++++++++++++++++++++++++++++++++++++++++ client/chart/chart.css | 22 +++++ client/chart/index.js | 5 + client/index.js | 6 +- client/map/index.js | 1 + client/tables.js | 2 +- 7 files changed, 235 insertions(+), 4 deletions(-) create mode 100644 client/chart/chart.container.js create mode 100644 client/chart/chart.css create mode 100644 client/chart/index.js (limited to 'client') diff --git a/client/applet.js b/client/applet.js index 27191693..cd73925c 100644 --- a/client/applet.js +++ b/client/applet.js @@ -4,6 +4,7 @@ import { Container as FaceSearchContainer } from './faceSearch' import { Container as FaceAnalysisContainer } from './faceAnalysis' import { Container as NameSearchContainer } from './nameSearch' import { Container as DatasetListContainer } from './datasetList' +import { Container as ChartContainer } from './chart' export default class Applet extends Component { render() { @@ -17,6 +18,8 @@ export default class Applet extends Component { return case 'dataset_list': return + case 'chart': + return default: return
{'Megapixels'}
} diff --git a/client/chart/chart.container.js b/client/chart/chart.container.js new file mode 100644 index 00000000..cbfc4de1 --- /dev/null +++ b/client/chart/chart.container.js @@ -0,0 +1,200 @@ +import React, { Component } from 'react' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' +import { toArray } from '../util' +import C3Chart from 'react-c3js' +import 'c3/c3.css' +import './chart.css' + +const rainbow = [ + '#9e0142', + '#d53e4f', + '#f46d43', + '#fdae61', + '#fee08b', + '#e6f598', + '#abdda4', + '#66c2a5', + '#3288bd', + '#5e4fa2', + // '#888888', +] + +const colorblindSafeRainbow = [ + '#a50026', + '#d73027', + '#f46d43', + '#fdae61', + '#fee090', + '#e0f3f8', + '#abd9e9', + '#74add1', + '#4575b4', + '#313695', + // '#888888', +] + +const topCountryCount = 9 + +const otherCountriesLabel = 'Other Countries' + +class ChartContainer extends Component { + state = { + } + + render() { + const { payload } = this.props + const { paper, citations } = payload.data + if (!citations.length) return null + const years = {} + const countries = {} + citations.forEach(citation => { + let { year, addresses } = citation + if (!year || !parseInt(year)) return + year = parseInt(year) + years[year] = years[year] || {} + addresses.forEach(address => { + const { country } = address + if (!country) return + if (!(country in countries)) { + countries[country] = 1 + } else { + countries[country] += 1 + } + if (country in years[year]) { + years[year][country] += 1 + } else { + years[year][country] = 1 + countries[country] = true + } + }) + }) + let yearList = Object.keys(years).map(year => parseInt(year)).sort() + for (let i = yearList[0]; i < yearList[-1]; i++) { + if (!(i in years)) { + years[i] = {} + } + } + yearList = Object.keys(years).map(year => parseInt(year)).sort() + + Object.keys(countries).forEach(country => { + yearList.forEach(year => { + if (!(country in years[year])) years[year][country] = 0 + }) + }) + + // figure out the top N countries in the dataset + const countriesByCount = Object.keys(countries).sort((a,b) => countries[b] - countries[a]) + // console.log(countriesByCount) + let topCountries = countriesByCount.slice(0, topCountryCount) + let otherCountries = countriesByCount.slice(topCountryCount) + + let citationCountsByYear = + [ ['x'].concat(yearList.map(year => String(year))) ] + .concat( + topCountries + .map(country => + [country] + .concat( + yearList + .map(year => years[year][country]) + ) + ) + ) + + citationCountsByYear.push( + [otherCountriesLabel].concat( + yearList.map(year => otherCountries.reduce((a,b) => (a + years[year][b]), 0)) + ) + ) + + const maxCitationsInAYear = citationCountsByYear + .slice(1) + .reduce((a,b) => ( + Math.max( + a, + b.slice(1).reduce((a2, b2) => ( + a2 + b2 + ), + 0) + ) + ), 0) + + let ticks = [] + for (let i = 0; i < maxCitationsInAYear; i += 50) { + ticks.push(i) + } + if (ticks[ticks.length - 1] < maxCitationsInAYear) { + ticks.push('') + } + + const colorPattern = rainbow + + return ( +
+ [country, countriesByYearLookup[country]]).sort((a,b) => b[1] - a[1]) + let topCountriesForThisYear = countriesByYear.slice(0, topCountryCount) + let bottomTotal = countriesByYear.slice(topCountryCount).reduce((a,b) => (a + b[1]), 0) + console.log(topCountriesForThisYear) + topCountriesForThisYear.push([otherCountriesLabel, bottomTotal]) + const tableRows = topCountriesForThisYear.map(([country, total]) => { + let colorIndex = topCountries.indexOf(country) + if (colorIndex < 0) colorIndex = colorPattern.length - 1 + const color = colorPattern[ colorIndex ] + return [ + "", + "", + "", + country, + "", + "", + total, + "", + "", + ].join('') + }) + return [ + "", + ...tableRows, + "
", + ].join('') + } + }} + /> +
+ ) + } +} + +export default ChartContainer diff --git a/client/chart/chart.css b/client/chart/chart.css new file mode 100644 index 00000000..f9c33247 --- /dev/null +++ b/client/chart/chart.css @@ -0,0 +1,22 @@ +.chart text { + fill: white; +} +.chart line { + stroke: white; +} +.chart path { + stroke: white; +} +.c3 path, +.c3 line { + stroke: white; +} + +.c3-tooltip, +.c3-tooltip td { + background: rgba(0,0,0,0.8); +} +.c3-tooltip th { + font-family: 'Roboto', sans-serif; + background: black; +} diff --git a/client/chart/index.js b/client/chart/index.js new file mode 100644 index 00000000..e9d3322d --- /dev/null +++ b/client/chart/index.js @@ -0,0 +1,5 @@ +import Container from './chart.container' + +export { + Container, +} diff --git a/client/index.js b/client/index.js index 1e4e2eb8..87214925 100644 --- a/client/index.js +++ b/client/index.js @@ -80,14 +80,14 @@ function runApplets() { url = null } } - if (('datasets' in payload) && !dataset && !url) { + if ((('datasets' in payload) && !dataset && !url) || window.location.pathname.match('datasets')) { const path = window.location.pathname.split('/').filter(s => !!s) - if (path.length) { + if (path.length > 1) { dataset = path.pop() if (dataset === 'index.html') { dataset = path.pop() } - // console.log('dataset from path:', dataset) + console.log('dataset from path:', dataset) } else { console.log('couldnt determine citations dataset') return null diff --git a/client/map/index.js b/client/map/index.js index ec9ebe66..9f4cc623 100644 --- a/client/map/index.js +++ b/client/map/index.js @@ -71,6 +71,7 @@ function addArc(map, src, dest, arcStyle) { export default function append(el, payload) { const { data } = payload + if (!data) return let { paper, address, citations } = data let source = [0, 0] diff --git a/client/tables.js b/client/tables.js index 851f76f5..1077289f 100644 --- a/client/tables.js +++ b/client/tables.js @@ -57,9 +57,9 @@ export default function append(el, payload) { // let path = payload.opt // console.log(path, columns) - console.log(payload.cmd, payload.url, payload.dataset) if (payload.cmd === 'citations') { let { data } = payload + if (!data) return null const citations = getCitations(data) // console.log(citations) table.setData(citations) -- cgit v1.2.3-70-g09d2