diff --git a/react-app/package.json b/react-app/package.json index c24afdc..c116f55 100644 --- a/react-app/package.json +++ b/react-app/package.json @@ -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", @@ -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", diff --git a/react-app/src/components/HighlightBarChart.js b/react-app/src/components/HighlightBarChart.js new file mode 100644 index 0000000..69d4c48 --- /dev/null +++ b/react-app/src/components/HighlightBarChart.js @@ -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 + } else { + return '' + } + } + + getSecondSelectBox = () => { + + if (this.props.store.highlightField !== 'none') { + + const { classes } = this.props + + return ( +
+ + + None + + {this.props.store.fieldNames.map((data, i) => { + return ( + + {data} + + ) + })} + +
+ ) + } 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; i7) + shownBarCount = 7 + let highlightField = '' + for (let i=0; i + ) + } else { + return '' + } + } + + handleFieldNameHighlighting = action( (event) => { + this.highlightSecondaryField = event.target.value + }) + + render() { + + return ( +
+ {this.getBarChart()} + {this.getSecondSelectBox()} + {this.getSecondBarChart()} +
+ ) + } +}) + +const styles = {} + +export default withStyles(styles)(HighlightBarChart) diff --git a/react-app/src/components/HighlightPieChart.js b/react-app/src/components/HighlightPieChart.js new file mode 100644 index 0000000..1fcfb32 --- /dev/null +++ b/react-app/src/components/HighlightPieChart.js @@ -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 + {this.props.store.highlightField} + + + } + + + return ( +
+ {pieChart} +
+ ) + } +}) + +const styles = () => ({}) + +export default withStyles(styles, { withTheme: true })(HighlightPieChart) diff --git a/react-app/src/components/MapDrawer.js b/react-app/src/components/MapDrawer.js index 28bdc7a..e5affdb 100644 --- a/react-app/src/components/MapDrawer.js +++ b/react-app/src/components/MapDrawer.js @@ -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' @@ -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 - {this.props.store.highlightField} - - - } - - if (this.props.store.selected.records && this.props.store.selected.records.length > 0) { return ( - {pieChart} - + {this.props.store.selected.records.length} records at selected location. {this.props.store.selected.records.map((record, i) => ( @@ -123,7 +101,7 @@ const MapDrawer = observer(class extends React.Component { }} square={true} > - {pieChart} + Select a location for more detail. diff --git a/react-app/src/components/MapFilter.js b/react-app/src/components/MapFilter.js index b860cfd..cc06d6b 100644 --- a/react-app/src/components/MapFilter.js +++ b/react-app/src/components/MapFilter.js @@ -151,7 +151,7 @@ const MapFilter = observer(class extends React.Component { -1) + if ([1,3,4].indexOf(i) > -1) colour = 'default' if (i<7) { return ( diff --git a/react-app/src/index.css b/react-app/src/index.css index 43cf152..c5e5176 100644 --- a/react-app/src/index.css +++ b/react-app/src/index.css @@ -3,56 +3,56 @@ body { } .search0 { - color: #885154; - background-color: lightgoldenrodyellow; + background-color: #885154; + color: #ffffff; font-weight: bold; } .searchIcon0 { fill: #885154; } .search1 { - color: #EC635F; - background-color: lightgoldenrodyellow; + background-color: #EC635F; + color: #000000; font-weight: bold; } .searchIcon1 { fill: #EC635F; } .search2 { - color: #76AAA1; - background-color: slategray; + background-color: #76AAA1; + color: #ffffff; font-weight: bold; } .searchIcon2 { fill: #76AAA1; } .search3 { - color: #e88735; - background-color: lightgoldenrodyellow; + background-color: #e88735; + color: #000000; font-weight: bold; } .searchIcon3 { fill: #e88735; } .search4 { - color: #91AF94; - background-color: lightgoldenrodyellow; + background-color: #91AF94; + color: #000000; font-weight: bold; } .searchIcon4 { fill: #91AF94; } .search5 { - color: #8C8C8C; - background-color: lightgoldenrodyellow; + background-color: #8C8C8C; + color: #ffffff; font-weight: bold; } .searchIcon5 { fill: #8C8C8C; } .search6 { - color: #465955; - background-color: lightgoldenrodyellow; + background-color: #465955; + color: #ffffff; font-weight: bold; } .searchIcon6 {