From 0e8abad29d1f1c1b658d5fd0106c7c23a52820d6 Mon Sep 17 00:00:00 2001 From: Eduardo Fischer Date: Tue, 23 Aug 2022 19:28:31 -0300 Subject: [PATCH] =?UTF-8?q?[#23]=20legenda=20din=C3=A2mica=20da=20colora?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20n=C3=B3s=20de=20universidades,=20programas?= =?UTF-8?q?=20e=20autores?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/App.scss | 11 +++-- app/src/components/AuthorGraph.jsx | 46 ++++++++++--------- app/src/components/AuthorTypeOverlay.jsx | 4 +- app/src/components/GraphLegend.jsx | 20 ++++++++ app/src/components/ProgramGraph.jsx | 21 ++++++--- app/src/components/UniversityGraph.jsx | 17 +++++-- .../{3d_graph_helper.js => graph_helper.js} | 9 +++- 7 files changed, 88 insertions(+), 40 deletions(-) create mode 100644 app/src/components/GraphLegend.jsx rename app/src/helpers/{3d_graph_helper.js => graph_helper.js} (81%) diff --git a/app/src/App.scss b/app/src/App.scss index 2d2caeb..f9c0a05 100644 --- a/app/src/App.scss +++ b/app/src/App.scss @@ -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 { @@ -96,7 +97,8 @@ $overlay-text-color: #ecf0f1; small { color: #bdc3c7; - margin: -0.1rem 0 0.6rem 0; + margin-top: -0.4rem; + display: block; } } @@ -142,7 +144,7 @@ $overlay-text-color: #ecf0f1; flex-direction: column; gap: 1rem; - .author-type-overlay { + .legend-overlay { @include overlay; display: flex; @@ -150,11 +152,10 @@ $overlay-text-color: #ecf0f1; gap: 5px; width: 100; - .type-option { + .legend-item { display: flex; gap: 10px; align-items: center; - cursor: pointer; .color-circle { height: 16px; diff --git a/app/src/components/AuthorGraph.jsx b/app/src/components/AuthorGraph.jsx index 6b9e59e..fcdf79e 100644 --- a/app/src/components/AuthorGraph.jsx +++ b/app/src/components/AuthorGraph.jsx @@ -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' @@ -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); @@ -41,6 +42,7 @@ function AuthorGraph() { .then(data => { setData(data) setIsLoading(false) + setTimeout(() => setLegendData(getLegendData(data, COLOR_BY_PROP)), 300) }) }, [university, programs]) @@ -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) @@ -93,7 +95,7 @@ function AuthorGraph() {
- + { selectedAuthor && }
@@ -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({ @@ -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)} /> diff --git a/app/src/components/AuthorTypeOverlay.jsx b/app/src/components/AuthorTypeOverlay.jsx index 06caffe..24ee2fb 100644 --- a/app/src/components/AuthorTypeOverlay.jsx +++ b/app/src/components/AuthorTypeOverlay.jsx @@ -9,7 +9,7 @@ function AuthorTypeOverlay({ enabledTypes, setEnabledTypes }) { } return ( -
+

Categorias

Clique em uma categoria para ocultá-la @@ -17,7 +17,7 @@ function AuthorTypeOverlay({ enabledTypes, setEnabledTypes }) {
toggleType(type)} - className={`type-option ${enabledTypes.includes(type) ? '' : ' disabled'}`} + className={`legend-item ${enabledTypes.includes(type) ? '' : ' disabled'}`} >
{type} diff --git a/app/src/components/GraphLegend.jsx b/app/src/components/GraphLegend.jsx new file mode 100644 index 0000000..4e6d4b8 --- /dev/null +++ b/app/src/components/GraphLegend.jsx @@ -0,0 +1,20 @@ +import React from 'react' + +function GraphLegend({legendData}) { + if (!legendData) return
+ + return ( +
+

Legenda

+ + {Object.entries(legendData).map(([type, color]) => ( +
+
+ {type} +
+ ))} +
+ ) +} + +export default GraphLegend \ No newline at end of file diff --git a/app/src/components/ProgramGraph.jsx b/app/src/components/ProgramGraph.jsx index 0b59d81..15f789a 100644 --- a/app/src/components/ProgramGraph.jsx +++ b/app/src/components/ProgramGraph.jsx @@ -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); @@ -37,6 +41,7 @@ function Graph() { .then(data => { setData(data) setIsLoading(false) + setTimeout(() => setLegendData(getLegendData(data, COLOR_BY_PROP)), 300) }) }, [university]) @@ -46,15 +51,19 @@ function Graph() { return (
-
- -
+
+ +
{ isLoading &&
} +
+ +
+ { const radius = sphereRadius(node.prod_count) * 4; const group = new THREE.Group(); diff --git a/app/src/components/UniversityGraph.jsx b/app/src/components/UniversityGraph.jsx index c124f66..ded89c5 100644 --- a/app/src/components/UniversityGraph.jsx +++ b/app/src/components/UniversityGraph.jsx @@ -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); @@ -34,6 +38,7 @@ function Graph() { .then(data => { setData(data) setIsLoading(false) + setTimeout(() => setLegendData(getLegendData(data, COLOR_BY_PROP)), 300) }) }, []); @@ -44,6 +49,10 @@ function Graph() {
} +
+ +
+ { const radius = sphereRadius(node.prod_count) * 1.5; const group = new THREE.Group(); diff --git a/app/src/helpers/3d_graph_helper.js b/app/src/helpers/graph_helper.js similarity index 81% rename from app/src/helpers/3d_graph_helper.js rename to app/src/helpers/graph_helper.js index 14a3b2d..6f5cb31 100644 --- a/app/src/helpers/3d_graph_helper.js +++ b/app/src/helpers/graph_helper.js @@ -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 } \ No newline at end of file