Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filtering functionality in legends #180

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
19 changes: 9 additions & 10 deletions components/FilterPanel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ const FilterPanel = ({

const changeGraph = name => {
// console.log('Changegraph', graph, patients.byId)
let currentFilter = _.find(filters, function(o) {
let currentFilter = _.find(filters, function (o) {
return o.name === filter
})
let choosenFilter = _.find(filters, function(o) {
let choosenFilter = _.find(filters, function (o) {
return o.name === name
})

Expand All @@ -137,14 +137,13 @@ const FilterPanel = ({
<HeaderContainer>
<FilterHeader>Cluster Filter</FilterHeader>
<FilterMenuContainer>
{filters.map((filterItem, filterIndex) => (
<Fragment key={filterIndex}>
<FilterCategory
filter={filterItem}
onClick={() => changeGraph(filterItem.name)}
selected={filter === filterItem.name ? true : false}
/>
</Fragment>
{filters.map((filterItem, index) => (
<FilterCategory
key={index}
filter={filterItem}
onClick={() => changeGraph(filterItem.name)}
selected={filter === filterItem.name ? true : false}
/>
))}
</FilterMenuContainer>
</HeaderContainer>
Expand Down
62 changes: 49 additions & 13 deletions components/NetworkMap/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import React, { useState, useEffect, useRef, useMemo } from 'react'
import Graph from 'react-graph-vis'
import { Tooltip, TooltipArrow, TooltipInner } from 'styled-tooltip-component'
import { connect, useSelector } from 'react-redux'
import { updateGraph, updatePatients, updateLastRefreshed, selectPatient, updateStates } from '../Redux/actions'
import { updateGraph, updatePatients, updateLastRefreshed, selectPatient, updateStates, updateSidePanelPatient, updateLegendFilter } from '../Redux/actions'

import { rowsToGraph, letterToCode } from '../../util/parse'
import normalize from '../../util/normalize'
import DatePicker from '../DatePicker'
import NetworkMapLegend from '../NetworkMapLegend'

const NetworkMap = ({
filter,
graph,
updateGraph,
updatePatients,
Expand All @@ -22,6 +22,10 @@ const NetworkMap = ({
}) => {
const graphRef = useRef()
const [isLoading, setIsLoading] = useState(true)
const [graphData, setGraphData] = useState({
nodes: [],
edges: []
})
const [toolTipPosition, setToolTipPosition] = useState(null)
const [tooltipContent, setToolTipContent] = useState('')
const toolTipVisible = useMemo(() => {
Expand All @@ -32,15 +36,15 @@ const NetworkMap = ({
selected: state.patient,
}))
useEffect(() => {
if(!states){
if (!states) {
fetch('https://api.covid19india.org/state_district_wise.json', {
cors: 'no-cors',
method: 'GET',
redirect: 'follow',
})
.then(resp => resp.json())
.then(res => {
if(res){
if (res) {
let stateNames = Object.keys(res);
updateStates(stateNames);
}
Expand All @@ -64,10 +68,10 @@ const NetworkMap = ({
.catch(err => console.log('error', err))
}, [isLoading])

// This effect handles zoom behaviour when a patient node is clicked
useEffect(() => {
// TODO: Figure out a way to make this do-able with patient Id search
if (graphRef.current && selected.coords) {
// Make sure the ref is ready
if (graphRef.current && selected && selected.coords) { // Make sure the ref is ready
const moveParams = {
position: selected.coords,
scale: 1.5,
Expand All @@ -81,6 +85,7 @@ const NetworkMap = ({
}
}, [selected])

// This effect handles zoom behaviour when search term is changed
useEffect(() => {
// TODO: Add search by age, district, etc.
if (graphRef.current && searchTerm) {
Expand All @@ -96,9 +101,18 @@ const NetworkMap = ({
}
}, [searchTerm])


useEffect(() => {
// setGraphData(graph)
if (graphRef.current) {
graphRef.current.Network.setData(graph)
}
}, [graph])

const options = {
layout: {
hierarchical: false,
clusterThreshold: 1300
},
edges: {
color: '#000000',
Expand All @@ -116,10 +130,22 @@ const NetworkMap = ({
navigationButtons: true,
hover: true,
},
physics: {
solver: 'forceAtlas2Based',
forceAtlas2Based: {
avoidOverlap: 1
}
// stabilization: true,
// barnesHut: {
// // gravitationalConstant: -80000,
// springConstant: 0.001,
// springLength: 200
// }
},
}

const events = {
select: function(event) {
select: function (event) {
const selectedNodeId = event.nodes[0]
const selectedNode = graph.nodes.find(v => v.id === selectedNodeId)
if (selectedNode) {
Expand All @@ -134,7 +160,7 @@ const NetworkMap = ({
}
}
},
hoverNode: function(e) {
hoverNode: function (e) {
const { node, event } = e
const selectedNode = graph.nodes.find(v => v.id === node)
setToolTipContent(selectedNode.label)
Expand All @@ -143,20 +169,30 @@ const NetworkMap = ({
left: event.pageX,
})
},
blurNode: function(event) {
blurNode: function (event) {
setToolTipContent('')
setToolTipPosition(null)
},
// stabilizationProgress: function (e) {
// document.getElementById('graph-loader').style.display = 'block'
// },
// stabilizationIterationsDone: function (e) {
// document.getElementById('graph-loader').style.display = 'none'
// }

}

return (
<div style={{ height: '100vh', width: '100vw' }}>
{isLoading ? null : (
<>
<NetworkMapLegend currentFilter={filter} />
{/* <div id="graph-loader" style={{ display: 'none', position: 'absolute', top: '50%', left: '50%', height: '100%', width: '100%', backgroundColor: 'white' }}>
Loading....
</div> */}
<NetworkMapLegend />
<Graph
ref={graphRef}
graph={graph}
graph={graphData}
options={options}
events={events}
/>
Expand All @@ -180,8 +216,8 @@ const NetworkMap = ({
}

const mapStateToProps = state => {
let { graph, searchTerm, filter, states } = state
return { graph, searchTerm, filter, states}
let { graph, searchTerm, states } = state
return { graph, searchTerm, states }
}

export default connect(mapStateToProps, {
Expand Down
126 changes: 83 additions & 43 deletions components/NetworkMapLegend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {
plane_abroad_node,
plane_local_node,
} from '../../images/index'

import { connect, useSelector } from 'react-redux'
import { updateLegendFilter, updateGraph, updateSidePanelPatient } from '../Redux/actions'
import { filterPatients } from '../../util/filters'

const LegendContainer = styled.div`
position: absolute;
Expand Down Expand Up @@ -45,7 +47,7 @@ const ImageContainer = styled.div`
display: flex;
align-items: center;
margin-bottom: 0.5rem;

cursor: pointer;

& :last-of-type {
margin-bottom: 0;
Expand All @@ -62,51 +64,89 @@ const Label = styled.span`
}
`

const NetworkMapLegend = ({ currentFilter }) => {
const patientTypes = [
{
label: 'Recovered',
image: male_cured,
forGlobalFilters: ['All']
},
{
label: 'Hospitalized',
image: male_hosp,
forGlobalFilters: ['All']
},
{
label: 'Deceased',
image: male_dead,
forGlobalFilters: ['All']
},
{
label: 'State',
image: state_node,
forGlobalFilters: ['State', 'City']
},
{
label: 'City',
image: city_node,
forGlobalFilters: ['City']
},
{
label: 'Domestic',
image: plane_local_node,
forGlobalFilters: ['Travel']
},
{
label: 'International',
image: plane_abroad_node,
forGlobalFilters: ['Travel']
},
]







const NetworkMapLegend = ({ globalFilter, updateLegendFilter, updateGraph, updateSidePanelPatient, legendFilter, graph, patients }) => {



const legendClickHandler = (term) => {
if (legendFilter !== term && ['Recovered', 'Hospitalized', 'Deceased'].includes(term)) {

let newGraph = filterPatients(graph, patients.byId, term, globalFilter)
updateGraph(newGraph)

updateLegendFilter(term)
// update side panel patient to show the correct patient(patient should be one from the visible list of nodes)
updateSidePanelPatient(newGraph.nodes[0].id)

}
}

const patientLegend = (item) => {
if (item.forGlobalFilters.includes('All') || item.forGlobalFilters.includes(globalFilter)) {
return (
<ImageContainer key={item.label} onClick={() => legendClickHandler(item.label)}>
<Image src={item.image} />
<Label style={{fontWeight: item.label === legendFilter ? 'bold' : 'normal'}}>{item.label}</Label>
</ImageContainer>
)
}
}


return (
<LegendContainer>
<ImageContainer>
<Image src={male_cured} />
<Label>Recovered</Label>
</ImageContainer>
<ImageContainer>
<Image src={male_hosp} />
<Label>Hospitalized</Label>
</ImageContainer>
<ImageContainer>
<Image src={male_dead} />
<Label>Deceased</Label>
</ImageContainer>
{['State', 'City'].includes(currentFilter) ?
<ImageContainer>
<Image src={state_node} />
<Label>State</Label>
</ImageContainer>
: null
}
{currentFilter === 'City' ?
<ImageContainer>
<Image src={city_node} />
<Label>City</Label>
</ImageContainer>
: null
}
{currentFilter === 'Travel' ?
<>
<ImageContainer>
<Image src={plane_local_node} />
<Label>Domestic</Label>
</ImageContainer>
<ImageContainer>
<Image src={plane_abroad_node} />
<Label>International</Label>
</ImageContainer>
</>
: null
}
{patientTypes.map(item => patientLegend(item))}
</LegendContainer>
)
}

const mapStateToProps = state => {
let { legendFilter, graph, patients, filter } = state
return { legendFilter, graph, patients, globalFilter: filter }
}

export default NetworkMapLegend;
export default connect(mapStateToProps, { updateLegendFilter, updateGraph, updateSidePanelPatient })(NetworkMapLegend);
2 changes: 2 additions & 0 deletions components/Redux/actionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ export default {
SELECT_PATIENT: 'SELECT_PATIENT',
SELECT_FILTER: 'SELECT_FILTER',
SEARCH: 'SEARCH',
LEGEND_FILTER: 'LEGEND_FILTER',
UPDATE_SIDEPANEL_PATIENT: 'UPDATE_SIDEPANEL_PATIENT',
UPDATE_STATES: 'UPDATE_STATES',
}
19 changes: 18 additions & 1 deletion components/Redux/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ const setSearchTerm = term => (dispatch, getState) => {
})
}

const updateLegendFilter = term => (dispatch, getState) => {
dispatch({
type: actionTypes.LEGEND_FILTER,
payload: {
term
}
})
}

const updateSidePanelPatient = patientId => (dispatch, getState) => {
dispatch({
type: actionTypes.UPDATE_SIDEPANEL_PATIENT,
payload: {
patientId
}
})
}
const updateStates = states => (dispatch, getState) => {
console.log('Action updateStates', states);
dispatch({
Expand All @@ -75,4 +92,4 @@ const updateStates = states => (dispatch, getState) => {
}

// Export the actions.
export { updateGraph, updatePatients, updateLastRefreshed, selectPatient, selectFilter, setSearchTerm, updateStates}
export { updateGraph, updatePatients, updateLastRefreshed, selectPatient, selectFilter, setSearchTerm, updateLegendFilter, updateSidePanelPatient, updateStates }
Loading