Skip to content

Commit

Permalink
[#23] legenda dinâmica da coloração de nós de universidades, programa…
Browse files Browse the repository at this point in the history
…s e autores
  • Loading branch information
Eduardo Fischer committed Aug 23, 2022
1 parent 3737551 commit 0e8abad
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 40 deletions.
11 changes: 6 additions & 5 deletions app/src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ $overlay-text-color: #ecf0f1;
font-size: 14pt;
text-transform: uppercase;
font-weight: bolder;
margin: 0
margin: 0;
margin-bottom: .5rem;
}

h2 {
Expand All @@ -96,7 +97,8 @@ $overlay-text-color: #ecf0f1;

small {
color: #bdc3c7;
margin: -0.1rem 0 0.6rem 0;
margin-top: -0.4rem;
display: block;
}
}

Expand Down Expand Up @@ -142,19 +144,18 @@ $overlay-text-color: #ecf0f1;
flex-direction: column;
gap: 1rem;

.author-type-overlay {
.legend-overlay {
@include overlay;

display: flex;
flex-direction: column;
gap: 5px;
width: 100;

.type-option {
.legend-item {
display: flex;
gap: 10px;
align-items: center;
cursor: pointer;

.color-circle {
height: 16px;
Expand Down
46 changes: 24 additions & 22 deletions app/src/components/AuthorGraph.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React, {useState, useEffect} from 'react'
import { ForceGraph3D } from 'react-force-graph'
import { getAuthorsCollabs, getAuthorData } from '../helpers/neo4j_helper'
import AuthorTypeOverlay from './AuthorTypeOverlay'
import authorTypeColorMap from '../config/author_type_colors.json'
import GraphLegend from './GraphLegend'
import { useCallback, useRef } from 'react'
import SpriteText from 'three-spritetext'
import AuthorInfoOverlay from './AuthorInfoOverlay'
Expand All @@ -11,17 +10,19 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faArrowLeft, faSpinner } from '@fortawesome/free-solid-svg-icons'
import { GlobalContext } from '../App'
import {
sphereRadius, setZoomLevel, setLinkForce, setChargeForce, setCenterForce,
} from '../helpers/3d_graph_helper'
sphereRadius, setZoomLevel, setLinkForce, setChargeForce, setCenterForce, getLegendData
} from '../helpers/graph_helper'
import * as THREE from 'three'

const COLOR_BY_PROP = 'type'

function AuthorGraph() {
const [enabledTypes, setEnabledTypes] = useState(Object.keys(authorTypeColorMap))
const [data, setData] = useState({nodes: [], links: []})
const [authorData, setAuthorData] = useState(undefined)
const [selectedAuthor, setSelectedAuthor] = useState(undefined)
const [windowDimensions, setWindowDimensions] = useState({width: window.innerWidth, height: window.innerHeight})
const [isLoading, setIsLoading] = useState(true)
const [legendData, setLegendData] = useState(undefined)
const fgRef = useRef();

const { university, programs, setPrograms } = React.useContext(GlobalContext);
Expand All @@ -41,6 +42,7 @@ function AuthorGraph() {
.then(data => {
setData(data)
setIsLoading(false)
setTimeout(() => setLegendData(getLegendData(data, COLOR_BY_PROP)), 300)
})
}, [university, programs])

Expand All @@ -62,16 +64,16 @@ function AuthorGraph() {
}
}, [selectedAuthor])

const isNodeVisible = useCallback(node => {
const nodeType = node.type
return nodeType ? enabledTypes.includes(node.type) : true
}, [enabledTypes])
// const isNodeVisible = useCallback(node => {
// const nodeType = node.type
// return nodeType ? enabledTypes.includes(node.type) : true
// }, [enabledTypes])

const isLinkVisible = useCallback(link => {
const sourceType = link.source.type
const targetType = link.target.type
return sourceType && targetType ? enabledTypes.includes(sourceType) && enabledTypes.includes(targetType) : true
}, [enabledTypes])
// const isLinkVisible = useCallback(link => {
// const sourceType = link.source.type
// const targetType = link.target.type
// return sourceType && targetType ? enabledTypes.includes(sourceType) && enabledTypes.includes(targetType) : true
// }, [enabledTypes])

const handleBackButton = () => {
if (selectedAuthor)
Expand All @@ -93,7 +95,7 @@ function AuthorGraph() {
</div>

<section className='right-panel'>
<AuthorTypeOverlay enabledTypes={enabledTypes} setEnabledTypes={setEnabledTypes} />
<GraphLegend legendData={legendData}/>
{ selectedAuthor && <AuthorInfoOverlay author={selectedAuthor} authorData={authorData} selectAuthor={setSelectedAuthor} /> }
</section>

Expand All @@ -104,9 +106,9 @@ function AuthorGraph() {
graphData={authorData || data}
nodeVal='prod_count'
nodeLabel={node => `${node.name} (${node.university})`}
nodeAutoColorBy='university'
nodeThreeObject={node => {
const radius = sphereRadius(node.prod_count) * 7;
nodeAutoColorBy={COLOR_BY_PROP}
nodeThreeObject={node => {
const radius = sphereRadius(node.prod_count) * 8;
const group = new THREE.Group();
const geometry = new THREE.SphereGeometry(radius);
const material = new THREE.MeshLambertMaterial({
Expand All @@ -125,12 +127,12 @@ function AuthorGraph() {
return group;
}}
linkColor='#d2dae2'
linkOpacity={selectedAuthor ? 0.2 : 0.1}
linkWidth={node => node.collabs_count / 5}
linkOpacity={0.2}
linkWidth={node => node.collabs_count / 2}
backgroundColor='#1e272e'
enableNodeDrag={true}
nodeVisibility={isNodeVisible}
linkVisibility={isLinkVisible}
// nodeVisibility={isNodeVisible}
// linkVisibility={isLinkVisible}
onNodeClick={setSelectedAuthor}
onBackgroundClick={() => setSelectedAuthor(undefined)}
/>
Expand Down
4 changes: 2 additions & 2 deletions app/src/components/AuthorTypeOverlay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ function AuthorTypeOverlay({ enabledTypes, setEnabledTypes }) {
}

return (
<div className='author-type-overlay'>
<div className='legend-overlay'>
<h1>Categorias</h1>
<small>Clique em uma categoria para ocultá-la</small>

{Object.entries(authorTypeColorMap).map(([type, color]) => (
<div
key={type}
onClick={() => toggleType(type)}
className={`type-option ${enabledTypes.includes(type) ? '' : ' disabled'}`}
className={`legend-item ${enabledTypes.includes(type) ? '' : ' disabled'}`}
>
<div className='color-circle' style={{backgroundColor: color}}/>
<span className='type-name'>{type}</span>
Expand Down
20 changes: 20 additions & 0 deletions app/src/components/GraphLegend.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react'

function GraphLegend({legendData}) {
if (!legendData) return <div/>

return (
<div className='legend-overlay'>
<h1>Legenda</h1>

{Object.entries(legendData).map(([type, color]) => (
<div key={type} className={`legend-item`}>
<div className='color-circle' style={{backgroundColor: color}}/>
<span className='type-name'>{type}</span>
</div>
))}
</div>
)
}

export default GraphLegend
21 changes: 15 additions & 6 deletions app/src/components/ProgramGraph.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ import SpriteText from 'three-spritetext'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faArrowLeft, faSpinner } from '@fortawesome/free-solid-svg-icons'
import { GlobalContext } from '../App'
import GraphLegend from './GraphLegend'
import {
sphereRadius, setZoomLevel, setLinkForce, setChargeForce, setCenterForce,
} from '../helpers/3d_graph_helper'
sphereRadius, setZoomLevel, setLinkForce, setChargeForce, setCenterForce, getLegendData
} from '../helpers/graph_helper'

import * as THREE from 'three'

const COLOR_BY_PROP = 'wide_knowledge_area'

function Graph() {
const [data, setData] = useState({nodes: [], links: []})
const [windowDimensions, setWindowDimensions] = useState({width: window.innerWidth, height: window.innerHeight})
const [isLoading, setIsLoading] = useState(true)
const [legendData, setLegendData] = useState(undefined)
const fgRef = useRef();

const { university, setUniversity, setPrograms } = React.useContext(GlobalContext);
Expand All @@ -37,6 +41,7 @@ function Graph() {
.then(data => {
setData(data)
setIsLoading(false)
setTimeout(() => setLegendData(getLegendData(data, COLOR_BY_PROP)), 300)
})
}, [university])

Expand All @@ -46,23 +51,27 @@ function Graph() {

return (
<section className='graph'>
<div className='back-button' onClick={handleBackButton}>
<FontAwesomeIcon icon={faArrowLeft}/>
</div>
<div className='back-button' onClick={handleBackButton}>
<FontAwesomeIcon icon={faArrowLeft}/>
</div>

{ isLoading &&
<div className='graph-loading'>
<FontAwesomeIcon icon={faSpinner} spin />
</div> }

<section className='right-panel'>
<GraphLegend legendData={legendData}/>
</section>

<ForceGraph3D
ref={fgRef}
width={windowDimensions.width}
height={windowDimensions.height - 50} // 50 is the height of the header
graphData={data}
nodeVal='prod_count'
nodeLabel='name'
nodeAutoColorBy='wide_knowledge_area'
nodeAutoColorBy={COLOR_BY_PROP}
nodeThreeObject={node => {
const radius = sphereRadius(node.prod_count) * 4;
const group = new THREE.Group();
Expand Down
17 changes: 13 additions & 4 deletions app/src/components/UniversityGraph.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ import SpriteText from 'three-spritetext'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
import { GlobalContext } from '../App'
import GraphLegend from './GraphLegend'
import {
sphereRadius, setZoomLevel, setLinkForce, setChargeForce, setCenterForce
} from '../helpers/3d_graph_helper'
sphereRadius, setZoomLevel, setLinkForce, setChargeForce, setCenterForce, getLegendData
} from '../helpers/graph_helper'
import * as THREE from 'three'

const COLOR_BY_PROP = 'region'

function Graph() {
const [data, setData] = useState({nodes: [], links: []})
const [windowDimensions, setWindowDimensions] = useState({width: window.innerWidth, height: window.innerHeight})
const [isLoading, setIsLoading] = useState(true)
const fgRef = useRef();
const [legendData, setLegendData] = useState(undefined)
const fgRef = useRef()

const { setUniversity } = React.useContext(GlobalContext);

Expand All @@ -34,6 +38,7 @@ function Graph() {
.then(data => {
setData(data)
setIsLoading(false)
setTimeout(() => setLegendData(getLegendData(data, COLOR_BY_PROP)), 300)
})
}, []);

Expand All @@ -44,14 +49,18 @@ function Graph() {
<FontAwesomeIcon icon={faSpinner} spin />
</div> }

<section className='right-panel'>
<GraphLegend legendData={legendData}/>
</section>

<ForceGraph3D
ref={fgRef}
width={windowDimensions.width}
height={windowDimensions.height - 50} // 50 is the height of the header
graphData={data}
nodeVal='prod_count'
nodeLabel='name'
nodeAutoColorBy='region'
nodeAutoColorBy={COLOR_BY_PROP}
nodeThreeObject={node => {
const radius = sphereRadius(node.prod_count) * 1.5;
const group = new THREE.Group();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,15 @@ export function setCenterForce(forceGraph, strength) {
forceGraph.d3Force('center', forceCenter().strength(strength));
}


// Calcula o raio de uma esfera com base no volume
export function sphereRadius(volume) {
return Math.cbrt(3*volume/(4*Math.PI))
}

export function getLegendData(data, colorByProperty) {
const legendData = {}
for (let node of data.nodes) {
legendData[node[colorByProperty] || 'NÃO INFORMADO'] = node.color
}
return legendData
}

0 comments on commit 0e8abad

Please sign in to comment.