From 87b11f2f1a44e10e2521671494fa425820edc5b2 Mon Sep 17 00:00:00 2001 From: Michael Behrisch <mbehrisch@graphpolaris.com> Date: Wed, 27 Nov 2024 18:47:37 +0100 Subject: [PATCH] fix: :bug: fixes arcplot story imports for dummy data --- .../smallVillainDoubleArchQueryResults.json | 2 + .../smallVillainQueryResults.json | 4 +- .../arcplotvis/arcplotvis.stories.tsx | 79 +++++++++--- .../visualizations/arcplotvis/arcplotvis.tsx | 115 +++++++++++++----- .../arcplotvis/components/arcplotd3nodes.tsx | 5 +- .../nodelinkvis/components/NLPixi.tsx | 34 ++---- 6 files changed, 164 insertions(+), 75 deletions(-) diff --git a/libs/shared/lib/mock-data/query-result/smallVillainDoubleArchQueryResults.json b/libs/shared/lib/mock-data/query-result/smallVillainDoubleArchQueryResults.json index f44cd8d75..7b6e853f4 100644 --- a/libs/shared/lib/mock-data/query-result/smallVillainDoubleArchQueryResults.json +++ b/libs/shared/lib/mock-data/query-result/smallVillainDoubleArchQueryResults.json @@ -2,12 +2,14 @@ "nodes": [ { "_id": "agent/007", + "label": "agent", "attributes": { "name": "Daniel Craig" } }, { "_id": "villain", + "label": "villain", "attributes": { "name": "Le Chiffre" } diff --git a/libs/shared/lib/mock-data/query-result/smallVillainQueryResults.json b/libs/shared/lib/mock-data/query-result/smallVillainQueryResults.json index f9e4514f0..663727f5a 100644 --- a/libs/shared/lib/mock-data/query-result/smallVillainQueryResults.json +++ b/libs/shared/lib/mock-data/query-result/smallVillainQueryResults.json @@ -2,12 +2,14 @@ "nodes": [ { "_id": "agent/007", + "label": "agent", "attributes": { "name": "Daniel Craig" } }, { - "_id": "villain", + "_id": "villain/Le Chiffre", + "label": "villain", "attributes": { "name": "Le Chiffre" } diff --git a/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.stories.tsx b/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.stories.tsx index efff52c98..a6056333d 100644 --- a/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.stories.tsx +++ b/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.stories.tsx @@ -2,8 +2,9 @@ import { configureStore } from '@reduxjs/toolkit'; import { Meta } from '@storybook/react'; import { Provider } from 'react-redux'; import { graphQueryResultSlice, querybuilderSlice, schemaSlice, visualizationSlice } from '../../../data-access/store'; -import { mockLargeQueryResults } from '../../../mock-data/query-result'; +import { mockData } from '../../../mock-data'; import ArcPlotComponent from './arcplotvis'; +// import { mockLargeQueryResults } from '@graphpolaris/shared/lib/mock-data'; const Mockstore = configureStore({ reducer: { @@ -33,33 +34,68 @@ const Component: Meta<typeof ArcPlotComponent.component> = { ], }; -export const SimpleData = { +export const SmallVillainDoubleArchData = { args: { - data: { - nodes: [ - { _id: 'agent/007', attributes: { name: 'Daniel Craig' } }, - { _id: 'villain', attributes: { name: 'Le Chiffre' } }, - ], - edges: [ - { - attributes: { - Type: 'PERSUES', - }, - from: 'agent/007', - _id: '14514', - to: 'villain', - }, - ], + ...(await mockData.smallVillainDoubleArchQueryResults()), + // configuration: ArcPlotComponent.configuration, + updateSettings: () => {}, + settings: { + ...ArcPlotComponent.settings, + }, + }, +}; + +export const SmallVillainData = { + args: { + ...(await mockData.smallVillainQueryResults()), + // configuration: ArcPlotComponent.configuration, + updateSettings: () => {}, + settings: { + ...ArcPlotComponent.settings, + }, + }, +}; + +export const GotCharacter2CharacterData = { + args: { + ...(await mockData.gotCharacter2Character()), + updateSettings: () => {}, + settings: { + ...ArcPlotComponent.settings, + }, + }, +}; + +export const movie_recommendationPersonActedInMovieQueryResultData = { + args: { + ...(await mockData.movie_recommendationPersonActedInMovieQueryResult()), + updateSettings: () => {}, + settings: { + ...ArcPlotComponent.settings, }, - configuration: ArcPlotComponent.configuration, }, }; export const LargeData = { args: { - data: mockLargeQueryResults, - configuration: ArcPlotComponent.configuration, + ...(await mockData.mockLargeQueryResults()), + updateSettings: () => {}, + settings: { + ...ArcPlotComponent.settings, + }, }, + + // args: { + // ...(await mockData.marieBoucherSample()), + // updateSettings: () => {}, + // schema: marieBoucherSampleSchemaRaw, + // settings: { + // ...PaohVisComponent.settings, + // rowNode: 'merchant', + // columnNode: 'merchant', + // attributeRowShow: ['_id', '# Connections', 'name2', 'birth'], + // }, + // }, }; export const Empty = { @@ -69,6 +105,9 @@ export const Empty = { edges: [], }, configuration: ArcPlotComponent.configuration, + settings: { + ...ArcPlotComponent.settings, + }, }, }; diff --git a/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx b/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx index accdeb1fa..871147d65 100644 --- a/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx +++ b/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx @@ -1,26 +1,29 @@ import { Input } from '@graphpolaris/shared/lib/components/inputs'; +import { Edge, Node } from '@graphpolaris/shared/lib/data-access'; +import { GraphStatistics } from '@graphpolaris/shared/lib/statistics'; import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config'; -import { select, selectAll } from 'd3'; +import { scaleOrdinal, schemeCategory10, select, selectAll } from 'd3'; import React, { useEffect, useMemo, useRef } from 'react'; import { VISComponentType, VisualizationPropTypes } from '../../common'; -import ArcVisNodes from './components/arcplotd3nodes'; -import { Edge, Node } from '@graphpolaris/shared/lib/data-access'; -import { scaleOrdinal, schemeCategory10 } from 'd3'; import { ArcEdge, ArcNode } from './arcplotvis.types'; import ArcVisLine from './components/arcplotd3line'; import ArcVisLinks from './components/arcplotd3links'; -import { GraphStatistics } from '@graphpolaris/shared/lib/statistics'; +import ArcVisNodes from './components/arcplotd3nodes'; export interface ArcVisProps { - iconStyle: string; - ordering: string; - labels: boolean; + iconStyle?: string; + ordering?: string; + showLabels?: boolean; + labelattribute?: string; + sizeAttributeSelector?: string; } -const configuration: ArcVisProps = { +const settings: ArcVisProps = { iconStyle: 'circle', - labels: true, - ordering: 'Name', + ordering: 'Label', + showLabels: true, + labelattribute: 'test', + sizeAttributeSelector: 'test', }; export const configStyle = { @@ -32,7 +35,8 @@ export const configStyle = { const margin = { top: 20, right: 20, bottom: 20, left: 20 }; -export const ArcPlotVis = React.memo(({ data, settings }: VisualizationPropTypes) => { +export const ArcPlotVis = React.memo(({ data, settings, graphMetadata }: VisualizationPropTypes) => { + console.log(data, settings, graphMetadata); const svgRef = useRef<SVGSVGElement>(null); const [dimensions, setDimensions] = React.useState({ width: 0, height: 0 }); @@ -40,8 +44,6 @@ export const ArcPlotVis = React.memo(({ data, settings }: VisualizationPropTypes const handleResize = () => { const width = svgRef.current?.clientWidth; const height = svgRef.current?.clientHeight; - // console.log('width', width); - // console.log('height', height); const dim = { width: width || 0, height: height || 0 }; setDimensions(dim); @@ -55,32 +57,71 @@ export const ArcPlotVis = React.memo(({ data, settings }: VisualizationPropTypes }; }, []); - const getNodeOrdering = (node: Node) => { - switch (settings.ordering) { - case 'Name': - return node.label.charCodeAt(0) * 10; - case 'Group': + const getNodeOrdering = (node: Node, index: number) => { + if (!settings.ordering) throw new Error('No ordering selected in settings: ' + settings.toString()); + + const OFFSET = 15; + + switch (settings.ordering.toLowerCase()) { + case 'label': + if (!node.label || node.label.length === 0) throw new Error('Node has no label or label is empty'); + return node.label.charCodeAt(0) * 10 + index * OFFSET; + case 'group': return margin.left + Math.random() * (dimensions.width - margin.right); - case 'Frequency': + case 'frequency': return margin.left + Math.random() * (dimensions.width - margin.right); default: return margin.left + Math.random() * (dimensions.width - margin.right); } }; + function getNodeLabel(node: Node): string { + if (!settings.labelattribute) throw new Error('No label attribute selected in settings: ' + settings.toString()); + + switch (settings.labelattribute.toLowerCase()) { + case 'label': + return node.label; + case 'type': + return node.attributes['type'] as string; + default: { + if (!node.label || node.label.length === 0) throw new Error('Node has no label or label is empty'); + // console.log('node get label', node); + return node.label; + } + } + } + + function getNodeSize(node: Node): number { + if (!settings.sizeAttributeSelector) throw new Error('No label attribute selected in settings: ' + settings.toString()); + + return Math.random() * 10; + + // switch (settings.labelattribute) { + // case 'name': + // return node.label; + // case 'type': + // return node.attributes['Type'] as string; + // default: { + // if (!node.label || node.label.length === 0) throw new Error('Node has no label or label is empty'); + // // console.log('node get label', node); + // return node.label; + // } + // } + } + const [dataNodes, dataLinks] = useMemo(() => { const color = scaleOrdinal(schemeCategory10).domain(data.nodes.map((node) => node.label)); const colorScale = scaleOrdinal(schemeCategory10).domain(data.edges.map((edge) => edge.attributes.Type as string)); const dataNodes = data.nodes.map((node, index) => { - let x = getNodeOrdering(node); + let x = getNodeOrdering(node, index); return { ...node, x: x, y: dimensions.height / 2, - size: Math.random() * 10, + size: getNodeSize(node), fill: color(node.label), - label: 'N' + index, + label: getNodeLabel(node), } as ArcNode; }) as ArcNode[]; @@ -187,6 +228,11 @@ export const ArcPlotVis = React.memo(({ data, settings }: VisualizationPropTypes return ( <svg ref={svgRef} className="h-full w-full"> {<ArcVisLine height={dimensions.height / 2} start={margin.left} end={dimensions.width - margin.right} />} + { + <g transform={`translate(0, ${dimensions.height / 2})`}> + <ArcVisLinks nodes={dataNodes} edges={dataLinks} height={dimensions.height} /> + </g> + } { <ArcVisNodes nodes={dataNodes} @@ -196,11 +242,6 @@ export const ArcPlotVis = React.memo(({ data, settings }: VisualizationPropTypes drawLabels={settings.labels} /> } - { - <g transform={`translate(0, ${dimensions.height / 2})`}> - <ArcVisLinks nodes={dataNodes} edges={dataLinks} height={dimensions.height} /> - </g> - } </svg> ); }); @@ -221,7 +262,7 @@ const ArcPlotVisSettings = ({ label="Select an Ordering" value={configuration.ordering} options={['Group', 'Frequency', 'Name']} - onChange={(val) => updateSettings({ theme: val })} + onChange={(val) => updateSettings({ ordering: val })} /> <Input type="dropdown" @@ -230,6 +271,20 @@ const ArcPlotVisSettings = ({ options={['circle', 'square', 'triangle']} onChange={(val) => updateSettings({ iconStyle: val })} /> + + <Input + type="boolean" + label="Show Label" + value={configuration.showLabels !== undefined ? configuration.showLabels : true} + onChange={(val) => updateSettings({ labels: val as boolean })} + /> + <Input + type="dropdown" + label="Label" + value={configuration.labelattribute} + options={['circle', 'square', 'triangle']} + onChange={(val) => updateSettings({ iconStyle: val })} + /> {/* <Input type="checkbox" label="Draw Labels" value={configuration.labels} onChange={(val) => updateSettings({ labels: val })} /> */} </SettingsContainer> ); @@ -240,7 +295,7 @@ const ArcPlotVisSettings = ({ export const ArcVisComponent: VISComponentType = { component: ArcPlotVis, settingsComponent: ArcPlotVisSettings, - settings: configuration, + settings: settings, exportImage: () => {}, }; export default ArcVisComponent; diff --git a/libs/shared/lib/vis/visualizations/arcplotvis/components/arcplotd3nodes.tsx b/libs/shared/lib/vis/visualizations/arcplotvis/components/arcplotd3nodes.tsx index 4edd3df5f..32cb96c06 100644 --- a/libs/shared/lib/vis/visualizations/arcplotvis/components/arcplotd3nodes.tsx +++ b/libs/shared/lib/vis/visualizations/arcplotvis/components/arcplotd3nodes.tsx @@ -65,9 +65,10 @@ const ArcVisNodes: React.FC<ArcPlotD3NodeProps> = ({ {drawLabels && ( <text x={node.x} - y={node.y + 25} - transform={`rotate(90 ${node.x + 5} ${node.y + 15})`} + y={node.y + 20} + transform={`rotate(45 ${node.x + 5} ${node.y + 15})`} fontSize={node.size} + style={{ fontFamily: 'monospace' }} className={`${styles.label}`} > {node.label} diff --git a/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx index d39dd29fa..0d235142c 100644 --- a/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx +++ b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx @@ -1,29 +1,19 @@ -import { GraphType, GraphTypeD3, LinkType, LinkTypeD3, NodeType, NodeTypeD3 } from '../types'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip'; +import { VisualizationTooltip } from '@graphpolaris/shared/lib/components/VisualizationTooltip'; +import { useConfig } from '@graphpolaris/shared/lib/data-access/store'; +import { Theme } from '@graphpolaris/shared/lib/data-access/store/configSlice'; import { dataColors, visualizationColors } from 'config'; -import { useEffect, useImperativeHandle, useMemo, useRef, useState, forwardRef } from 'react'; -import { Application, Container, FederatedPointerEvent, Graphics, IPointData, Sprite, Text, RenderTexture } from 'pixi.js'; -import { useAppDispatch, useML, useSearchResultData } from '../../../../data-access'; -import { NLPopUp } from './NLPopup'; -import { hslStringToHex, nodeColor } from './utils'; -import { - CytoscapeLayout, - GraphologyLayout, - LayoutFactory, - Layouts, - LayoutTypes, - GraphologyForceAtlas2Webworker, - AllLayoutAlgorithms, - AlgorithmToLayoutProvider, -} from '../../../../graph-layout'; import { MultiGraph } from 'graphology'; import { Viewport } from 'pixi-viewport'; -import { NodelinkVisProps } from '../nodelinkvis'; -import { Tooltip, TooltipContent, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip'; import { MovedEvent } from 'pixi-viewport/dist/types'; -import { VisualizationTooltip } from '@graphpolaris/shared/lib/components/VisualizationTooltip'; -import { nodeColorHex } from './utils'; -import { Theme } from '@graphpolaris/shared/lib/data-access/store/configSlice'; -import { useConfig } from '@graphpolaris/shared/lib/data-access/store'; +import { Application, Container, FederatedPointerEvent, Graphics, IPointData, RenderTexture, Sprite, Text } from 'pixi.js'; +import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; +import { useML, useSearchResultData } from '../../../../data-access'; +import { AllLayoutAlgorithms, GraphologyForceAtlas2Webworker, LayoutFactory, Layouts, LayoutTypes } from '../../../../graph-layout'; +import { NodelinkVisProps } from '../nodelinkvis'; +import { GraphType, GraphTypeD3, LinkType, LinkTypeD3, NodeType, NodeTypeD3 } from '../types'; +import { NLPopUp } from './NLPopup'; +import { hslStringToHex, nodeColor, nodeColorHex } from './utils'; type Props = { onClick: (event?: { node: NodeTypeD3; pos: IPointData }) => void; -- GitLab