Skip to content

Commit

Permalink
Select a pie slice to show those on the map #61
Browse files Browse the repository at this point in the history
* Separate the pie chart into its own component
* Create a new bar chart component
* Allow drilling down into a bar in the first bar chart
* Minor colour tweaks to make text more readable
  • Loading branch information
dylanett committed Mar 15, 2019
1 parent 70a19fa commit bd75359
Show file tree
Hide file tree
Showing 6 changed files with 340 additions and 41 deletions.
2 changes: 2 additions & 0 deletions react-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"FileReader": "^0.10.2",
"axios": "^0.17.1",
"chai": "^4.2.0",
"chart.js": "^2.7.3",
"eslint": "5.12.0",
"eslint-plugin-sort-imports-es6-autofix": "^0.3.0",
"fs": "0.0.1-security",
Expand All @@ -33,6 +34,7 @@
"mobx-react": "^4.3.3",
"papaparse": "^4.4.0",
"react": "^16.6.3",
"react-chartjs-2": "^2.7.4",
"react-dom": "^16.6.3",
"react-router-dom": "^4.2.2",
"react-scripts": "^2.1.1",
Expand Down
271 changes: 271 additions & 0 deletions react-app/src/components/HighlightBarChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
import {action, autorun, extendObservable} from 'mobx'
import { observer } from 'mobx-react'
import { withStyles } from '@material-ui/core'
import { HorizontalBar } from 'react-chartjs-2'
import _ from 'lodash'
import Layout from '../utils/Layout'
import MenuItem from '@material-ui/core/MenuItem'
import PropTypes from 'prop-types'
import React from 'react'
import TextField from '@material-ui/core/TextField'

const HighlightBarChart = observer(class extends React.Component {

static propTypes = {
theme: PropTypes.object.isRequired,
store: PropTypes.object.isRequired,
}

constructor() {
super()
extendObservable(this, {
highlightSecondaryText: 'none',
highlightSecondaryIndex: 0,
highlightSecondaryField: 'none',
})
}

/*
componentDidMount() {
this.autorunDisposer = autorun(() => {
if (this.props.store.highlightField === 'none') {
action( () => {
this.highlightSecondaryText = 'none'
this.highlightSecondaryIndex = 0
this.highlightSecondaryField = 'none'
})
}
})
}
componentWillUnmount() {
this.autorunDisposer()
}
*/

countFieldValue = (valueCounts, thisValue) => {
// Used for incrementing our count of a given value.
if (thisValue) { // We have a value
let counter = _.find(valueCounts, function(o) { return o.value === thisValue }) // See if we have a counter object already for this field value
if (counter) {
counter.count++
} else {
counter = { value: thisValue, count: 1 }
valueCounts.push(counter)
}
}
}

/**
* Generates a barchart based on the current highlightField in the map.
* @returns a barchart corresponding to the highlightField counts for different values.
*/
getBarChart = () => {
if (this.props.store.highlightField !== 'none') {
const barChartLabels = []
const barChartData = []

let shownBarCount = this.props.store.foundRecords.length
if (shownBarCount > 7)
shownBarCount = 7
let highlightField = ''
for (let i = 0; i < shownBarCount; i++) {
const searchString = this.props.store.searchStrings[i]
const separatorLocation = searchString.indexOf(':')
highlightField = searchString.substring(0, separatorLocation)
const highlightValue = searchString.substring(separatorLocation + 2)
barChartLabels.push(highlightValue)
barChartData.push(this.props.store.foundRecords[i].length)
}
/*
if (this.props.store.getOtherCount.get()>0) {
barChartLabels.push('Other')
barChartData.push(this.props.store.getOtherCount.get())
}
*/

const data = {
labels: barChartLabels,
datasets: [{
label: '',
backgroundColor: Layout.colours,
borderColor: 'rgb(255, 99, 132)',
data: barChartData,
}]
}

const options = {
legend: {
display: false,
},
onClick: action((event, elements) => {
if (elements[0]) {
this.highlightSecondaryText = elements[0]._model.label
this.highlightSecondaryIndex = elements[0]._index
}
}),
scales: {
xAxes: [{
ticks: {
beginAtZero: true,
}
}]
},
title: {
display: true,
text: highlightField,
},
}

return <HorizontalBar data={data} options={options}/>
} else {
return ''
}
}

getSecondSelectBox = () => {

if (this.props.store.highlightField !== 'none') {

const { classes } = this.props

return (
<div>
<TextField
id='secondary-select-field'
select
label='Secondary Field'
value={this.highlightSecondaryField}
onChange={this.handleFieldNameHighlighting}
SelectProps={{
MenuProps: {
className: classes.menu,
},
}}
margin='normal'
variant='outlined'
style={{ marginLeft: 10, width: 200 }}
>
<MenuItem key='none' value='none' name='none'>
None
</MenuItem>
{this.props.store.fieldNames.map((data, i) => {
return (
<MenuItem key={i} value={data} name={data}>
{data}
</MenuItem>
)
})}
</TextField>
</div>
)
} else {
return ''
}
}

/**
* Generates a secondary barchart showing details of the clicked bar in the first bar chart.
* @returns the jsx tag for the barchart if applicable.
*/
getSecondBarChart = () => {

console.log(this.props.store.highlightField)
console.log(this.highlightSecondaryText)
console.log(this.highlightSecondaryIndex)
console.log(this.highlightSecondaryField)

if (this.props.store.highlightField !== 'none' && this.highlightSecondaryField !== 'none' && this.highlightSecondaryIndex>0) {

const barChartLabels = []
const barChartData = []

// Here we have to get the counts of the values grouped by the second highlight field.
let valueCounts = [] // An array of objects used to keep a count of each of all of the values of this field, eg {value:'foo',count:7}
let thisValue

const secondaryHighlightField = this.highlightSecondaryField //'Occupation'

if (this.props.store.foundRecords.length >= this.highlightSecondaryIndex) {
this.props.store.foundRecords[this.highlightSecondaryIndex].forEach((record) => {
thisValue = _.get(record, secondaryHighlightField) // Try to find the value of the requested field in the current card
this.countFieldValue(valueCounts, thisValue)
})
}

valueCounts = _.sortBy(valueCounts, (o) => -o.count)
const secondaryStrings = []
for (let i=0; i<valueCounts.length; i++) {
if (valueCounts[i]) {
secondaryStrings.push(secondaryHighlightField + ': ' + valueCounts[i].value)
}
}


let shownBarCount = valueCounts.length
if (shownBarCount>7)
shownBarCount = 7
let highlightField = ''
for (let i=0; i<shownBarCount; i++) {
const searchString = secondaryStrings[i]
const separatorLocation = searchString.indexOf(':')
highlightField = searchString.substring(0, separatorLocation)
const highlightValue = searchString.substring(separatorLocation + 2)
barChartLabels.push(highlightValue)
barChartData.push(valueCounts[i].count)
}

const data = {
labels: barChartLabels,
datasets: [{
label: '',
backgroundColor: Layout.colours[this.highlightSecondaryIndex],
borderColor: 'rgb(255, 99, 132)',
data: barChartData,
}]
}

const options = {
legend: {
display: false,
},
scales: {
xAxes: [{
ticks: {
beginAtZero: true,
}
}]
},
title: {
display: true,
text: highlightField,
},
}

return (
<HorizontalBar data={data} options={options} />
)
} else {
return ''
}
}

handleFieldNameHighlighting = action( (event) => {
this.highlightSecondaryField = event.target.value
})

render() {

return (
<div>
{this.getBarChart()}
{this.getSecondSelectBox()}
{this.getSecondBarChart()}
</div>
)
}
})

const styles = {}

export default withStyles(styles)(HighlightBarChart)
48 changes: 48 additions & 0 deletions react-app/src/components/HighlightPieChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { observer } from 'mobx-react'
import { withStyles } from '@material-ui/core'
import Layout from '../utils/Layout'
import PieChart from 'react-simple-pie-chart'
import PropTypes from 'prop-types'
import React from 'react'
import Typography from '@material-ui/core/Typography'

const HighlightPieChart = observer(class extends React.Component {

static propTypes = {
theme: PropTypes.object.isRequired,
store: PropTypes.object.isRequired,
}

render() {

let pieChart = null

if (this.props.store.highlightField !== 'none') {
const pieChartData = []
let shownSliceCount = this.props.store.foundRecords.length
if (shownSliceCount>7)
shownSliceCount = 7
for (let i=0; i<shownSliceCount; i++) {
pieChartData.push({ color: Layout.colours[i], value: this.props.store.foundRecords[i].length })
}
pieChartData.push({ color: Layout.colours[7], value: this.props.store.getOtherCount.get() })
pieChart = <div>
<Typography variant='h5' align='center'>{this.props.store.highlightField}</Typography>
<PieChart
slices={pieChartData}
/>
</div>
}


return (
<div>
{pieChart}
</div>
)
}
})

const styles = () => ({})

export default withStyles(styles, { withTheme: true })(HighlightPieChart)
28 changes: 3 additions & 25 deletions react-app/src/components/MapDrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { withStyles } from '@material-ui/core'
import _ from 'lodash'
import Card from '@material-ui/core/Card'
import CardContent from '@material-ui/core/CardContent'
import Layout from '../utils/Layout'
import HighlightBarChart from './HighlightBarChart'
import linkifyHtml from 'linkifyjs/html'
import Paper from '@material-ui/core/Paper'
import PieChart from 'react-simple-pie-chart'
import PropTypes from 'prop-types'
import React from 'react'
import StreetviewCard from './StreetviewCard'
Expand Down Expand Up @@ -35,26 +34,6 @@ const MapDrawer = observer(class extends React.Component {
const { classes } = this.props
const { open } = this.state

let pieChart = null

if (this.props.store.highlightField !== 'none') {
const pieChartData = []
let shownSliceCount = this.props.store.foundRecords.length
if (shownSliceCount>7)
shownSliceCount = 7
for (let i=0; i<shownSliceCount; i++) {
pieChartData.push({ color: Layout.colours[i], value: this.props.store.foundRecords[i].length })
}
pieChartData.push({ color: Layout.colours[7], value: this.props.store.getOtherCount })
pieChart = <div>
<Typography variant='h5' align='center'>{this.props.store.highlightField}</Typography>
<PieChart
slices={pieChartData}
/>
</div>
}


if (this.props.store.selected.records && this.props.store.selected.records.length > 0) {
return (
<Paper
Expand All @@ -66,8 +45,7 @@ const MapDrawer = observer(class extends React.Component {
}}
square={true}
>
{pieChart}

<HighlightBarChart store={this.props.store} />
<Typography variant='h6' align='center'>{this.props.store.selected.records.length} records at selected location.</Typography>

{this.props.store.selected.records.map((record, i) => (
Expand Down Expand Up @@ -123,7 +101,7 @@ const MapDrawer = observer(class extends React.Component {
}}
square={true}
>
{pieChart}
<HighlightBarChart store={this.props.store} />
<Typography component='i' variant='h6' align='center'>
Select a location for more detail.
</Typography>
Expand Down
Loading

0 comments on commit bd75359

Please sign in to comment.