From e4a7a92b7692b8a71c29056b85e10e272f8e9d3e Mon Sep 17 00:00:00 2001
From: Sjoerd <svink@graphpolaris.com>
Date: Thu, 24 Oct 2024 12:42:44 +0000
Subject: [PATCH] feat: incorporate new statistics refactoring

---
 libs/shared/lib/components/inputs/index.tsx   |   2 +-
 .../lib/components/textEditor/TextEditor.tsx  |   2 +-
 .../textEditor/plugins/PreviewPlugin.tsx      |   7 +-
 .../lib/data-access/statistics/dimensions.ts  |  51 ------
 .../lib/data-access/statistics/index.ts       |   3 -
 .../lib/data-access/statistics/statistics.ts  | 168 ------------------
 .../statistics/statistics.types.ts            |  84 ---------
 .../store/graphQueryResultSlice.ts            |  36 +---
 libs/shared/lib/data-access/store/hooks.ts    |   6 +-
 libs/shared/lib/statistics/graphStatistics.ts |   5 +-
 libs/shared/lib/statistics/index.ts           |   1 +
 libs/shared/lib/vis/common/types.ts           |   6 +-
 .../visualizations/arcplotvis/arcplotvis.tsx  |  25 +--
 .../mapvis/components/MapSettings.tsx         |  17 +-
 .../layers/nodelink-layer/NodeLinkOptions.tsx |  10 +-
 .../vis/visualizations/mapvis/mapvis.types.ts |   4 +-
 .../configPanel/SemSubsConfigPanel.tsx        |  12 +-
 .../lib/vis/visualizations/vis1D/Vis1D.tsx    |   2 +-
 18 files changed, 61 insertions(+), 380 deletions(-)
 delete mode 100644 libs/shared/lib/data-access/statistics/dimensions.ts
 delete mode 100644 libs/shared/lib/data-access/statistics/index.ts
 delete mode 100644 libs/shared/lib/data-access/statistics/statistics.ts
 delete mode 100644 libs/shared/lib/data-access/statistics/statistics.types.ts

diff --git a/libs/shared/lib/components/inputs/index.tsx b/libs/shared/lib/components/inputs/index.tsx
index 57d4600de..58f207dff 100644
--- a/libs/shared/lib/components/inputs/index.tsx
+++ b/libs/shared/lib/components/inputs/index.tsx
@@ -34,7 +34,7 @@ type TextProps = {
   className?: string;
   validate?: (value: any) => boolean;
   onChange?: (value: string) => void;
-  onClick?: (e: React.MouseEvent<HTMLElement>) => void;
+  onClick?: (e: React.MouseEvent<HTMLInputElement>) => void;
 };
 
 type NumberProps = {
diff --git a/libs/shared/lib/components/textEditor/TextEditor.tsx b/libs/shared/lib/components/textEditor/TextEditor.tsx
index 9d71b27bb..f3ba1ce03 100644
--- a/libs/shared/lib/components/textEditor/TextEditor.tsx
+++ b/libs/shared/lib/components/textEditor/TextEditor.tsx
@@ -36,7 +36,7 @@ export function TextEditor({ editorState, setEditorState, showToolbar, placehold
     nodes: [VariableNode],
   };
 
-  const contentEditableRef = useRef();
+  const contentEditableRef = useRef<HTMLDivElement>(null);
 
   return (
     <LexicalComposer initialConfig={initialConfig}>
diff --git a/libs/shared/lib/components/textEditor/plugins/PreviewPlugin.tsx b/libs/shared/lib/components/textEditor/plugins/PreviewPlugin.tsx
index cb81db1ae..9ac8ccc21 100644
--- a/libs/shared/lib/components/textEditor/plugins/PreviewPlugin.tsx
+++ b/libs/shared/lib/components/textEditor/plugins/PreviewPlugin.tsx
@@ -1,10 +1,9 @@
-import { LegacyRef, useRef, useEffect, MutableRefObject } from 'react';
+import { LegacyRef, useRef, useEffect, MutableRefObject, RefObject } from 'react';
 import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
-import { FORMAT_TEXT_COMMAND } from 'lexical';
 import { Button } from '../../buttons';
 import { $generateHtmlFromNodes } from '@lexical/html';
 
-export function PreviewPlugin({ contentEditable }: { contentEditable: MutableRefObject<HTMLDivElement | undefined> }): JSX.Element {
+export function PreviewPlugin({ contentEditable }: { contentEditable: RefObject<HTMLDivElement | undefined> }): JSX.Element {
   const [editor] = useLexicalComposerContext();
 
   function updatePreview() {
@@ -47,7 +46,7 @@ export function PreviewPlugin({ contentEditable }: { contentEditable: MutableRef
     if (editor == null) return;
 
     editor.classList.remove('hidden');
-    editor.querySelector('div').focus();
+    editor?.querySelector('div')?.focus();
 
     let preview = editor.parentElement?.querySelector('.editor + .preview');
     if (preview == null) return;
diff --git a/libs/shared/lib/data-access/statistics/dimensions.ts b/libs/shared/lib/data-access/statistics/dimensions.ts
deleted file mode 100644
index 11e21cd34..000000000
--- a/libs/shared/lib/data-access/statistics/dimensions.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { DimensionType } from '@graphpolaris/shared/lib/schema';
-
-const isNumerical = (value: any): boolean => {
-  if (typeof value === 'number') return true;
-  const parsedNumber = parseFloat(value);
-  if (!isNaN(parsedNumber)) return true;
-  return false;
-};
-
-const isSpatial = (value: any): boolean => {
-  if (Array.isArray(value) && value.length === 2) {
-    const [latitude, longitude] = value;
-    if (typeof latitude === 'number' && typeof longitude === 'number') {
-      return true;
-    }
-  } else if (typeof value === 'object' && value !== null) {
-    if ('latitude' in value && 'longitude' in value) {
-      const { latitude, longitude } = value;
-      if (typeof latitude === 'number' && typeof longitude === 'number') {
-        return true;
-      }
-    }
-  }
-  return false;
-};
-
-// TODO: implement temporal classifier
-function isTemporal(value: any): boolean {
-  return false;
-  // const datetime = moment(value);
-  // if (datetime.isValid()) {
-  //   return true;
-  // }
-  // return false;
-}
-
-export const getDimension = (value: any): DimensionType => {
-  if (Array.isArray(value)) {
-    if (value.length === 0) return 'categorical';
-    return getDimension(value[0]);
-  }
-
-  if (isTemporal(value)) {
-    return 'temporal';
-  } else if (isSpatial(value)) {
-    return 'spatial';
-  } else if (isNumerical(value)) {
-    return 'numerical';
-  }
-  return 'categorical';
-};
diff --git a/libs/shared/lib/data-access/statistics/index.ts b/libs/shared/lib/data-access/statistics/index.ts
deleted file mode 100644
index 58f57b693..000000000
--- a/libs/shared/lib/data-access/statistics/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export { getDimension } from './dimensions';
-export { extractStatistics } from './statistics';
-export * from './statistics.types';
diff --git a/libs/shared/lib/data-access/statistics/statistics.ts b/libs/shared/lib/data-access/statistics/statistics.ts
deleted file mode 100644
index f44855884..000000000
--- a/libs/shared/lib/data-access/statistics/statistics.ts
+++ /dev/null
@@ -1,168 +0,0 @@
-import { DimensionType } from '@graphpolaris/shared/lib/schema';
-import { GraphMetadata, DimensionStatistics } from './statistics.types';
-import { CategoricalStats, NumericalStats, SpatialStats, TemporalStats } from './statistics.types';
-
-function extractCategoricalStats(data: any[]): CategoricalStats {
-  const uniqueItems = new Set<string>(data.map((item) => String(item).slice(0, 50)));
-  const uniqueItemCount = uniqueItems.size;
-
-  // Calculate mode
-  const frequencyMap: { [key: string]: number } = {};
-  data.forEach((item) => {
-    const strItem = String(item).slice(0, 100);
-    frequencyMap[strItem] = (frequencyMap[strItem] || 0) + 1;
-  });
-  const mode = Object.keys(frequencyMap).reduce((a, b) => (frequencyMap[a] > frequencyMap[b] ? a : b), '');
-
-  // Calculate entropy
-  const totalItems = data.length;
-  const entropy = -Object.values(frequencyMap).reduce((acc, frequency) => {
-    const probability = frequency / totalItems;
-    if (probability === 0) return acc;
-    return acc + probability * Math.log2(probability);
-  }, 0);
-
-  // Calculate Gini Index
-  const giniIndex =
-    1 -
-    Object.values(frequencyMap).reduce((acc, frequency) => {
-      const probability = frequency / totalItems;
-      if (probability === 0) return acc;
-      return acc + Math.pow(probability, 2);
-    }, 0);
-
-  return {
-    uniqueItems: uniqueItemCount,
-    mode,
-    entropy,
-    giniIndex,
-    values: Array.from(uniqueItems),
-  };
-}
-
-function extractNumericalStats(data: number[]): NumericalStats {
-  const n = data.length;
-  if (n === 0) {
-    return {
-      min: 0,
-      max: 0,
-      average: 0,
-      variance: 0,
-      std: 0,
-      kurtosis: 0,
-    };
-  }
-
-  const min = Math.min(...data);
-  const max = Math.max(...data);
-  const sum = data.reduce((acc, curr) => acc + curr, 0);
-  const average = sum / n;
-
-  let variance = 0;
-  for (const value of data) {
-    variance += Math.pow(value - average, 2);
-  }
-  variance /= n;
-  const std = Math.sqrt(variance);
-
-  let kurtosis = 0;
-  if (std !== 0) {
-    const fourthMoment = data.reduce((acc, curr) => acc + Math.pow(curr - average, 4), 0) / n;
-    kurtosis = fourthMoment / Math.pow(std, 4) - 3;
-  }
-
-  return {
-    min,
-    max,
-    average,
-    variance,
-    std,
-    kurtosis: Number.isFinite(kurtosis) ? kurtosis : 0,
-  };
-}
-
-function extractTemporalStats(data: Date[]): TemporalStats {
-  const sortedDates = data.slice().sort((a, b) => a.getTime() - b.getTime());
-  const minDate = sortedDates[0];
-  const maxDate = sortedDates[sortedDates.length - 1];
-  const range = maxDate.getTime() - minDate.getTime();
-
-  return {
-    minDate,
-    maxDate,
-    range,
-    minTimestamp: minDate.getTime(),
-    maxTimestamp: maxDate.getTime(),
-  };
-}
-
-function extractSpatialStats(data: [number, number][]): SpatialStats {
-  const totalPoints = data.length;
-  const sumCoordinates = data.reduce(([x1, y1], [x2, y2]) => [x1 + x2, y1 + y2], [0, 0]);
-  const middlePoint: [number, number] = [sumCoordinates[0] / totalPoints, sumCoordinates[1] / totalPoints];
-
-  const minX = Math.min(...data.map(([x, _]) => x));
-  const minY = Math.min(...data.map(([_, y]) => y));
-  const maxX = Math.max(...data.map(([x, _]) => x));
-  const maxY = Math.max(...data.map(([_, y]) => y));
-
-  const boundingBox: [[number, number], [number, number]] = [
-    [minX, minY],
-    [maxX, maxY],
-  ];
-
-  return {
-    middlePoint,
-    minX,
-    minY,
-    maxX,
-    maxY,
-    boundingBox,
-  };
-}
-
-export const extractStatistics = (summaryGraph: GraphMetadata): GraphMetadata => {
-  const graph = summaryGraph;
-
-  Object.entries(graph.nodes.types).forEach(([label, node]) => {
-    Object.entries(node.attributes).forEach(([id, object]) => {
-      const { values, dimension } = object;
-      const statistics = calculateStatistics(values || [], dimension);
-      graph.nodes.types[label].attributes[id] = { dimension, ...statistics };
-    });
-  });
-
-  Object.entries(graph.edges.types).forEach(([label, edge]) => {
-    Object.entries(edge.attributes).forEach(([id, object]) => {
-      const { values, dimension } = object;
-      const statistics = calculateStatistics(values || [], dimension);
-      graph.edges.types[label].attributes[id] = { dimension, ...statistics };
-    });
-  });
-
-  console.debug('Graph query statistics', summaryGraph);
-  return graph;
-};
-
-export const calculateStatistics = (item: any[], dimensionType: DimensionType) => {
-  let statistics: DimensionStatistics | undefined;
-
-  switch (dimensionType) {
-    case 'categorical':
-      statistics = extractCategoricalStats(item);
-      break;
-    case 'numerical':
-      statistics = extractNumericalStats(item);
-      break;
-    case 'spatial':
-      statistics = extractSpatialStats(item);
-      break;
-    case 'temporal':
-      statistics = extractTemporalStats(item);
-      break;
-    default:
-      statistics = extractCategoricalStats(item);
-  }
-
-  return statistics;
-};
diff --git a/libs/shared/lib/data-access/statistics/statistics.types.ts b/libs/shared/lib/data-access/statistics/statistics.types.ts
deleted file mode 100644
index ac170024e..000000000
--- a/libs/shared/lib/data-access/statistics/statistics.types.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import { DimensionType } from '../../schema';
-
-export type CategoricalStats = {
-  uniqueItems: number;
-  values: string[];
-  mode: string;
-  entropy: number;
-  giniIndex: number;
-};
-
-export type NumericalStats = {
-  min: number;
-  max: number;
-  average: number;
-  variance: number;
-  std: number;
-  kurtosis: number;
-};
-
-export type TemporalStats = {
-  minDate: Date;
-  maxDate: Date;
-  range: number;
-  minTimestamp: number;
-  maxTimestamp: number;
-};
-
-export type Coordinate = [number, number];
-
-export type SpatialStats = {
-  middlePoint: Coordinate;
-  minX: number;
-  minY: number;
-  maxX: number;
-  maxY: number;
-  boundingBox: [Coordinate, Coordinate];
-};
-
-export type DimensionStatistics = CategoricalStats | NumericalStats | TemporalStats | SpatialStats;
-
-export type DimensionInfoType = { [label: string]: { [attribute: string]: DimensionType } };
-
-export type DimensionMap = {
-  nodes: DimensionInfoType;
-  edges: DimensionInfoType;
-};
-
-export type ElementStats = {
-  dimension: DimensionType;
-  stats?: DimensionStatistics;
-};
-
-export type ElementStatisticsType = {
-  [label: string]: { [attribute: string]: ElementStats };
-};
-
-export interface GraphStatistics {
-  nodes: ElementStatisticsType;
-  edges: ElementStatisticsType;
-}
-
-export type CompressedElement = {
-  count?: number;
-  labels: string[];
-  types: {
-    [label: string]: {
-      count: number;
-      avgDegreeIn?: number;
-      avgDegreeOut?: number;
-      attributes: {
-        [id: string]: {
-          dimension: DimensionType;
-          values?: any[];
-          statistics?: any;
-        };
-      };
-    };
-  };
-};
-
-export type GraphMetadata = {
-  nodes: CompressedElement;
-  edges: CompressedElement;
-};
diff --git a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts
index a9f919a13..4f0870242 100755
--- a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts
+++ b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts
@@ -1,7 +1,6 @@
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
 import type { RootState } from './store';
-import { getDimension, extractStatistics, GraphMetadata } from '../statistics';
-import { getGraphStatistics } from '../../statistics';
+import { getGraphStatistics, GraphStatistics } from '../../statistics';
 
 export interface GraphQueryResultFromBackendPayload {
   queryID: string;
@@ -50,7 +49,7 @@ export type Edge = {
 
 // Define a type for the slice state
 export type GraphQueryResult = {
-  metaData?: GraphMetadata;
+  metaData?: GraphStatistics;
   nodes: Node[];
   edges: Edge[];
   queryingBackend: boolean;
@@ -79,11 +78,6 @@ export const graphQueryBackend2graphQuery = (payload: GraphQueryResultFromBacken
   let nodes = [...nodeIDs].map((nodeID) => payload.nodes.find((node) => node._id === nodeID) as unknown as Node);
   let edges = [...edgeIDs].map((edgeID) => payload.edges.find((edge) => edge._id === edgeID) as unknown as Edge);
 
-  const metaData: GraphMetadata = {
-    nodes: { labels: [], count: nodes.length, types: {} },
-    edges: { labels: [], count: edges.length, types: {} },
-  };
-
   nodes = nodes.map((node) => {
     let _node = { ...node };
     // TODO!: only works for neo4j
@@ -91,17 +85,6 @@ export const graphQueryBackend2graphQuery = (payload: GraphQueryResultFromBacken
     let innerLabels = node?.attributes?.labels as string[];
 
     if (innerLabels?.length > 0) nodeType = innerLabels[0] as string;
-    if (!metaData.nodes.labels.includes(nodeType)) metaData.nodes.labels?.push(nodeType);
-    if (!metaData.nodes.types[nodeType]) metaData.nodes.types[nodeType] = { count: 0, attributes: {} };
-    metaData.nodes.types[nodeType].count++;
-
-    Object.entries(_node.attributes).forEach(([attributeId, attributeValue]) => {
-      if (!metaData.nodes.types[nodeType].attributes[attributeId]) {
-        const dimension = getDimension(attributeValue);
-        metaData.nodes.types[nodeType].attributes[attributeId] = { dimension, values: [], statistics: {} };
-      }
-      metaData.nodes.types[nodeType].attributes[attributeId].values?.push(attributeValue);
-    });
 
     _node.label = nodeType;
     return _node;
@@ -113,23 +96,14 @@ export const graphQueryBackend2graphQuery = (payload: GraphQueryResultFromBacken
     let edgeType: string = edge._id.split('/')[0];
 
     if (!_edge._id.includes('/')) edgeType = edge.attributes.Type as string;
-    if (!metaData.edges.labels?.includes(edgeType)) metaData.edges.labels?.push(edgeType);
-    if (!metaData.edges.types[edgeType]) metaData.edges.types[edgeType] = { count: 0, attributes: {} };
-    metaData.edges.types[edgeType].count++;
-
-    Object.entries(_edge.attributes).forEach(([attributeId, attributeValue]) => {
-      if (!metaData.edges.types[edgeType].attributes[attributeId]) {
-        const dimension = getDimension(attributeValue);
-        metaData.edges.types[edgeType].attributes[attributeId] = { dimension, values: [], statistics: {} };
-      }
-      metaData.edges.types[edgeType].attributes[attributeId].values?.push(attributeValue);
-    });
 
     _edge.label = edgeType;
     return _edge;
   });
 
-  return { metaData: extractStatistics(metaData), nodes, edges };
+  const metaData = getGraphStatistics(payload);
+
+  return { metaData, nodes, edges };
 };
 
 export const graphQueryResultSlice = createSlice({
diff --git a/libs/shared/lib/data-access/store/hooks.ts b/libs/shared/lib/data-access/store/hooks.ts
index d55cbb791..9e7feb3fe 100644
--- a/libs/shared/lib/data-access/store/hooks.ts
+++ b/libs/shared/lib/data-access/store/hooks.ts
@@ -22,7 +22,7 @@ import {
   selectQuerybuilderHash,
 } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
 import { activeSaveState, activeSaveStateAuthorization, SessionCacheI, sessionCacheState } from './sessionSlice';
-import { AuthSliceState, UseIsAuthorizedState, authState } from './authSlice';
+import { AuthSliceState, authState } from './authSlice';
 import { visualizationState, VisState, visualizationActive } from './visualizationSlice';
 import { ML, allMLEnabled, selectML } from './mlSlice';
 import {
@@ -37,12 +37,12 @@ import {
 import { AllLayoutAlgorithms } from '../../graph-layout';
 import { QueryGraphEdgeHandle, QueryMultiGraph } from '../../querybuilder';
 import { SchemaGraph, SchemaGraphInference, SchemaGraphStats } from '../../schema';
-import { GraphMetadata } from '../statistics';
 import { SelectionStateI, FocusStateI, focusState, selectionState } from './interactionSlice';
 import { VisualizationSettingsType } from '../../vis/common';
 import { PolicyUsersState, selectPolicyState } from './authorizationUsersSlice';
 import { PolicyResourcesState, selectResourcesPolicyState } from './authorizationResourcesSlice';
 import { SaveStateAuthorizationHeaders, SaveStateI } from '..';
+import { GraphStatistics } from '../../statistics';
 
 // Use throughout your app instead of plain `useDispatch` and `useSelector`
 export const useAppDispatch: () => AppDispatch = useDispatch;
@@ -51,7 +51,7 @@ export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
 
 /** Gives the graphQueryResult from the store */
 export const useGraphQueryResult: () => GraphQueryResult = () => useAppSelector(selectGraphQueryResult);
-export const useGraphQueryResultMeta: () => GraphMetadata | undefined = () => useAppSelector(selectGraphQueryResultMetaData);
+export const useGraphQueryResultMeta: () => GraphStatistics | undefined = () => useAppSelector(selectGraphQueryResultMetaData);
 
 // Gives the schema
 export const useSchema: () => SchemaSliceI = () => useAppSelector(schema);
diff --git a/libs/shared/lib/statistics/graphStatistics.ts b/libs/shared/lib/statistics/graphStatistics.ts
index 53ead16bd..7f2db7998 100644
--- a/libs/shared/lib/statistics/graphStatistics.ts
+++ b/libs/shared/lib/statistics/graphStatistics.ts
@@ -8,8 +8,10 @@ const getGraphStatistics = (graph: GraphQueryResultFromBackend): GraphStatistics
   const n_nodes = nodes.length;
   const n_edges = edges.length;
 
+  const density = n_nodes < 2 ? 0 : (n_edges * 2) / (n_nodes * (n_nodes - 1));
+
   const metaData: GraphStatistics = {
-    topological: { density: (n_edges * 2) / (n_nodes * (n_nodes - 1)) || 0, self_loops: 0 },
+    topological: { density, self_loops: 0 },
     nodes: { labels: [], count: n_nodes, types: {} },
     edges: { labels: [], count: n_edges, types: {} },
   };
@@ -37,6 +39,7 @@ const getGraphStatistics = (graph: GraphQueryResultFromBackend): GraphStatistics
     });
   });
 
+  // Process edges
   edges.forEach((edge) => {
     const edgeType = getEdgeType(edge);
     if (!metaData.edges.labels.includes(edgeType)) {
diff --git a/libs/shared/lib/statistics/index.ts b/libs/shared/lib/statistics/index.ts
index e00f85d98..979311f96 100644
--- a/libs/shared/lib/statistics/index.ts
+++ b/libs/shared/lib/statistics/index.ts
@@ -1 +1,2 @@
 export * from './graphStatistics';
+export * from './statistics.types';
diff --git a/libs/shared/lib/vis/common/types.ts b/libs/shared/lib/vis/common/types.ts
index 3f2c302cb..118c0a933 100644
--- a/libs/shared/lib/vis/common/types.ts
+++ b/libs/shared/lib/vis/common/types.ts
@@ -3,9 +3,9 @@ import { GraphQueryResult } from '../../data-access/store/graphQueryResultSlice'
 import { ML } from '../../data-access/store/mlSlice';
 import { SchemaGraph } from '../../schema';
 import type { AppDispatch } from '../../data-access';
-import { GraphMetadata } from '../../data-access/statistics';
 import { Node, Edge } from '../../data-access/store/graphQueryResultSlice';
 import { Visualizations } from '../components/VisualizationPanel';
+import { GraphStatistics } from '../../statistics';
 
 export type VisualizationSettingsType = {
   id: string;
@@ -28,7 +28,7 @@ export type VisualizationPropTypes<T = {}> = {
   ml: ML;
   settings: T & VisualizationSettingsType;
   dispatch: AppDispatch;
-  graphMetadata: GraphMetadata;
+  graphMetadata: GraphStatistics;
   updateSettings: (newSettings: any) => void;
   handleHover: (val: any) => void;
   handleSelect: (selection?: { nodes?: Node[]; edges?: Edge[] }) => void;
@@ -36,7 +36,7 @@ export type VisualizationPropTypes<T = {}> = {
 
 export type VisualizationSettingsPropTypes<T = {}> = {
   settings: T & VisualizationSettingsType;
-  graphMetadata: GraphMetadata;
+  graphMetadata: GraphStatistics;
   updateSettings: (val: Partial<T>) => void;
 };
 
diff --git a/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx b/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx
index 8b7fad619..c7a336462 100644
--- a/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx
+++ b/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx
@@ -1,16 +1,15 @@
 import { Input } from '@graphpolaris/shared/lib/components/inputs';
-import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics';
 import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config';
 import { 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';
 
 export interface ArcVisProps {
   iconStyle: string;
@@ -33,7 +32,7 @@ export const configStyle = {
 
 const margin = { top: 20, right: 20, bottom: 20, left: 20 };
 
-export const ArcPlotVis = React.memo(({ data, configuration }: VisualizationPropTypes) => {
+export const ArcPlotVis = React.memo(({ data, settings }: VisualizationPropTypes) => {
   const svgRef = useRef<SVGSVGElement>(null);
   const [dimensions, setDimensions] = React.useState({ width: 0, height: 0 });
 
@@ -57,7 +56,7 @@ export const ArcPlotVis = React.memo(({ data, configuration }: VisualizationProp
   }, []);
 
   const getNodeOrdering = (node: Node) => {
-    switch (configuration.ordering) {
+    switch (settings.ordering) {
       case 'Name':
         return node.label.charCodeAt(0) * 10;
       case 'Group':
@@ -108,7 +107,7 @@ export const ArcPlotVis = React.memo(({ data, configuration }: VisualizationProp
     });
 
     return [dataNodes as ArcNode[], dataLinks as ArcEdge[]]; // MIB: no idea why typescript needs this cast so explicitly
-  }, [data, configuration, dimensions]);
+  }, [data, settings, dimensions]);
 
   const handleMouseEnterNodes = (event: React.MouseEvent<SVGGElement, MouseEvent>) => {
     const targetClassList = (event.currentTarget as SVGGElement).classList;
@@ -193,8 +192,8 @@ export const ArcPlotVis = React.memo(({ data, configuration }: VisualizationProp
           nodes={dataNodes}
           handleMouseEnter={handleMouseEnterNodes}
           handleMouseOut={handleMouseOutNodes}
-          iconStyle={configuration.iconStyle}
-          drawLabels={configuration.labels}
+          iconStyle={settings.iconStyle}
+          drawLabels={settings.labels}
         />
       }
       {
@@ -212,7 +211,7 @@ const ArcPlotVisSettings = ({
   updateSettings,
 }: {
   configuration: ArcVisProps;
-  graph: GraphMetadata;
+  graph: GraphStatistics;
   updateSettings: (val: any) => void;
 }) => {
   return (
@@ -236,10 +235,14 @@ const ArcPlotVisSettings = ({
   );
 };
 
+// remove ts ignore when added to Visualization Dict, because displayname needs to be key of it
+// @ts-ignore
 export const ArcVisComponent: VISComponentType = {
-  displayName: 'Arc Plot',
+  displayName: 'ArcPlot',
+  description: '',
   component: ArcPlotVis,
-  settings: ArcPlotVisSettings,
-  configuration: configuration,
+  settingsComponent: ArcPlotVisSettings,
+  settings: configuration,
+  exportImage: () => {},
 };
 export default ArcVisComponent;
diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/MapSettings.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/MapSettings.tsx
index 066629a48..317245c62 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/components/MapSettings.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/components/MapSettings.tsx
@@ -10,13 +10,16 @@ export const MapSettings = ({ settings, graphMetadata, updateSettings }: Visuali
   // For backwards compatibility with older saveStates, we migrate information from settings.nodes to settings.location
   // FIXME: this can be removed once all systems have updated their saveStates.
   if (!('location' in settings)) {
-    settings = JSON.parse(JSON.stringify(settings));  // Undo Object.preventExtensions()
+    settings = JSON.parse(JSON.stringify(settings)); // Undo Object.preventExtensions()
     settings.location = Object.entries(settings.nodes)
-      .map(([k, v]) => [k, {
-        lat: (v as any).lat,
-        lon: (v as any).lon
-      }]) 
-      .reduce((obj, [k, v]) => ({...obj, [k as string]: v}), {});
+      .map(([k, v]) => [
+        k,
+        {
+          lat: (v as any).lat,
+          lon: (v as any).lon,
+        },
+      ])
+      .reduce((obj, [k, v]) => ({ ...obj, [k as string]: v }), {});
   }
 
   const DataLayerSettings = settings.layer && layerSettings?.[settings.layer];
@@ -36,7 +39,7 @@ export const MapSettings = ({ settings, graphMetadata, updateSettings }: Visuali
       graphMetadata.nodes.labels.map((node) => [
         node,
         Object.entries(graphMetadata.nodes.types[node].attributes)
-          .filter(([, value]) => value.dimension === 'numerical')
+          .filter(([, value]) => value.attributeType === 'number')
           .map(([key]) => key),
       ]),
     );
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx
index 51f05d351..89f2fb108 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx
@@ -6,6 +6,7 @@ import { LayerSettingsComponentType } from '../../mapvis.types';
 import { nodeColorRGB } from '../../utils';
 import { Accordion, AccordionBody, AccordionHead, AccordionItem } from '@graphpolaris/shared/lib/components/accordion';
 import { isEqual } from 'lodash-es';
+import { CategoricalStats } from '@graphpolaris/shared/lib/statistics';
 
 const defaultNodeSettings = (index: number) => ({
   color: nodeColorRGB(index + 1),
@@ -156,7 +157,7 @@ export function NodeLinkOptions({
                                       [nodeType]: {
                                         ...nodeSettings,
                                         colorAttribute: String(val),
-                                        colorAttributeType: graphMetadata.nodes.types[nodeType].attributes[val].dimension,
+                                        colorAttributeType: graphMetadata.nodes.types[nodeType].attributes[val].attributeType,
                                       },
                                     },
                                   })
@@ -175,12 +176,13 @@ export function NodeLinkOptions({
                                   />
                                 </div>
                               ) : (
-                                nodeSettings.colorAttributeType === 'categorical' &&
+                                nodeSettings.colorAttributeType === 'string' &&
                                 nodeSettings.colorAttribute && (
                                   <div>
                                     {(
-                                      graphMetadata.nodes.types[nodeType] as { attributes: { [key: string]: { values: string[] } } }
-                                    )?.attributes?.[nodeSettings.colorAttribute]?.values.map((attr: string) => (
+                                      graphMetadata.nodes.types[nodeType]?.attributes?.[nodeSettings.colorAttribute]
+                                        ?.statistics as CategoricalStats
+                                    ).values.map((attr: string) => (
                                       <div key={attr} className="flex items-center justify-between">
                                         <p className="truncate w-18">{attr.length > 0 ? attr : 'Empty val'}</p>
                                         <ColorPicker
diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts
index fe4068e20..f8a5135c4 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts
+++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts
@@ -2,7 +2,7 @@ import { CompositeLayer } from 'deck.gl';
 import { VisualizationPropTypes, VisualizationSettingsType } from '../../common';
 import { MapProps } from './mapvis';
 import { Node as QueryNode } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
-import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics';
+import { GraphStatistics } from '@graphpolaris/shared/lib/statistics';
 
 export type Coordinate = [number, number];
 
@@ -53,7 +53,7 @@ export type LayerSettingsType = {
 
 export type LayerSettingsComponentType<T = {}> = {
   settings: T & VisualizationSettingsType;
-  graphMetadata: GraphMetadata;
+  graphMetadata: GraphStatistics;
   spatialAttributes: { [k: string]: string[] };
   updateSpatialAttribute: (label: string, attribute: 'lat' | 'lon', value: string) => void;
   updateLayerSettings: (val: Partial<LayerSettingsType>) => void;
diff --git a/libs/shared/lib/vis/visualizations/semanticsubstratesvis/configPanel/SemSubsConfigPanel.tsx b/libs/shared/lib/vis/visualizations/semanticsubstratesvis/configPanel/SemSubsConfigPanel.tsx
index 938906cd1..270b4fa28 100644
--- a/libs/shared/lib/vis/visualizations/semanticsubstratesvis/configPanel/SemSubsConfigPanel.tsx
+++ b/libs/shared/lib/vis/visualizations/semanticsubstratesvis/configPanel/SemSubsConfigPanel.tsx
@@ -2,13 +2,13 @@ import React, { useState, useEffect } from 'react';
 import { Button } from '@graphpolaris/shared/lib/components/buttons';
 import { Icon } from '@graphpolaris/shared/lib/components/icon';
 import { DataFromPanel, DataPanelConfig } from '../components/types';
-import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics';
 import { EntityPillSelector } from '@graphpolaris/shared/lib/components/selectors/entityPillSelector';
 import { Input } from '@graphpolaris/shared/lib/components';
+import { CategoricalStats, GraphStatistics } from '@graphpolaris/shared/lib/statistics';
 
 export type SemSubsConfigPanelProps = {
   dataFromPanel: DataFromPanel;
-  graphMetaData: GraphMetadata;
+  graphMetaData: GraphStatistics;
   colorNode: string;
   onUpdateData: (data: Partial<DataPanelConfig>) => void;
   onCollapse: (isOpen: boolean) => void;
@@ -72,11 +72,12 @@ export const SemSubsConfigPanel: React.FC<SemSubsConfigPanelProps> = ({
     categoricalKeys.push(undefined);
 
     for (const key in graphMetaData.nodes.types[data.entitySelected].attributes) {
-      const values = graphMetaData.nodes.types[data.entitySelected].attributes[key].values;
+      const stats = graphMetaData.nodes.types[data.entitySelected].attributes[key].statistics as CategoricalStats;
+      const values = stats?.values;
       if (values && values.length > 0 && values[0].toString() !== '[object Object]') {
         if (
           graphMetaData.nodes.types[data.entitySelected].attributes.hasOwnProperty(key) &&
-          graphMetaData.nodes.types[data.entitySelected].attributes[key].dimension === 'categorical'
+          graphMetaData.nodes.types[data.entitySelected].attributes[key].attributeType === 'string'
         ) {
           categoricalKeys.push(key as string);
         }
@@ -100,7 +101,8 @@ export const SemSubsConfigPanel: React.FC<SemSubsConfigPanelProps> = ({
     if (!data.entitySelected || !data.attributeSelected) return;
 
     if (data.attributeSelected && graphMetaData.nodes.types[data.entitySelected].attributes[data.attributeSelected]) {
-      let arrayAttributeValues = graphMetaData.nodes.types[data.entitySelected].attributes[data.attributeSelected].values;
+      let stats = graphMetaData.nodes.types[data.entitySelected].attributes[data.attributeSelected].statistics as CategoricalStats;
+      let arrayAttributeValues = stats?.values;
       if (arrayAttributeValues !== undefined) {
         if (arrayAttributeValues[0].toString() != '[object Object]') {
           setStateConfigPanelOptions((prevState) => ({
diff --git a/libs/shared/lib/vis/visualizations/vis1D/Vis1D.tsx b/libs/shared/lib/vis/visualizations/vis1D/Vis1D.tsx
index 926beff6a..42f8391d8 100644
--- a/libs/shared/lib/vis/visualizations/vis1D/Vis1D.tsx
+++ b/libs/shared/lib/vis/visualizations/vis1D/Vis1D.tsx
@@ -100,7 +100,7 @@ const Vis1DSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio
     if (graphMetadata && graphMetadata.nodes && graphMetadata.nodes.labels.length > 0 && settings.nodeLabel != '') {
       const newAttributeOptions = Object.entries(graphMetadata.nodes.types[settings.nodeLabel].attributes).map(([key, value]) => ({
         name: key,
-        type: value.dimension,
+        type: value.attributeType,
       }));
       updateSettings({ attribute: newAttributeOptions[0].name });
       // initialize the selected option for creating the dropdown and plots
-- 
GitLab