diff --git a/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx index 398badd3e456f76050d182015e82e228be1f9cc4..a14eaa6cffd4d17fcc94c45f8e80837046ea7d09 100644 --- a/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx +++ b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx @@ -1,8 +1,8 @@ import React, { useState } from 'react'; -import { dataColors } from 'config/src/colors.js'; import { DropdownButton, DropdownContainer, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns'; import ColorLegend from '../colorLegend/index.js'; import { DimensionType } from '@graphpolaris/shared/lib/schema/index.js'; +import { dataColors } from 'config'; type TailwindColor = { colors: string | { [key: number]: string }; diff --git a/libs/shared/lib/data-access/statistics/statistics.ts b/libs/shared/lib/data-access/statistics/statistics.ts index b967ca5efb477f29a8ad7129dd7de8e1ae9dfacd..f44855884766a5bc7a71483d0a6d12cfb7afa498 100644 --- a/libs/shared/lib/data-access/statistics/statistics.ts +++ b/libs/shared/lib/data-access/statistics/statistics.ts @@ -1,5 +1,5 @@ import { DimensionType } from '@graphpolaris/shared/lib/schema'; -import { GraphMetaData, DimensionStatistics } from './statistics.types'; +import { GraphMetadata, DimensionStatistics } from './statistics.types'; import { CategoricalStats, NumericalStats, SpatialStats, TemporalStats } from './statistics.types'; function extractCategoricalStats(data: any[]): CategoricalStats { @@ -121,7 +121,7 @@ function extractSpatialStats(data: [number, number][]): SpatialStats { }; } -export const extractStatistics = (summaryGraph: GraphMetaData): GraphMetaData => { +export const extractStatistics = (summaryGraph: GraphMetadata): GraphMetadata => { const graph = summaryGraph; Object.entries(graph.nodes.types).forEach(([label, node]) => { diff --git a/libs/shared/lib/data-access/statistics/statistics.types.ts b/libs/shared/lib/data-access/statistics/statistics.types.ts index 2ec1352e47ea2e958990c59acadf53607c29b960..ac170024eb7e5186037013876c787efcb1adee5c 100644 --- a/libs/shared/lib/data-access/statistics/statistics.types.ts +++ b/libs/shared/lib/data-access/statistics/statistics.types.ts @@ -78,7 +78,7 @@ export type CompressedElement = { }; }; -export type GraphMetaData = { +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 1273ebf443395d8acc781dab3fda7a3cbba6ae55..dc8cf265374db48ed5c8fdffaab5263a6fe7f133 100755 --- a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts +++ b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts @@ -1,6 +1,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import type { RootState } from './store'; -import { getDimension, extractStatistics, GraphMetaData } from '../statistics'; +import { getDimension, extractStatistics, GraphMetadata } from '../statistics'; export interface GraphQueryResultFromBackendPayload { queryID: string; @@ -49,7 +49,7 @@ export interface Edge { // Define a type for the slice state export interface GraphQueryResult { - metaData: GraphMetaData; + metaData: GraphMetadata; nodes: Node[]; edges: Edge[]; queryingBackend: boolean; @@ -78,7 +78,7 @@ 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 = { + const metaData: GraphMetadata = { nodes: { labels: [], count: nodes.length, types: {} }, edges: { labels: [], count: edges.length, types: {} }, }; diff --git a/libs/shared/lib/data-access/store/hooks.ts b/libs/shared/lib/data-access/store/hooks.ts index 2406ea03c8ebcd6c90c1b78f67222941bd67b7de..bcdce1c93276ac355f6604fbbad83ab3cae428a3 100644 --- a/libs/shared/lib/data-access/store/hooks.ts +++ b/libs/shared/lib/data-access/store/hooks.ts @@ -27,7 +27,7 @@ import { import { AllLayoutAlgorithms } from '../../graph-layout'; import { QueryMultiGraph } from '../../querybuilder'; import { SchemaGraph } from '../../schema'; -import { GraphMetaData } from '../statistics'; +import { GraphMetadata } from '../statistics'; // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch: () => AppDispatch = useDispatch; @@ -36,7 +36,7 @@ export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; /** Gives the graphQueryResult from the store */ export const useGraphQueryResult: () => GraphQueryResult = () => useAppSelector(selectGraphQueryResult); -export const useGraphQueryResultMeta: () => GraphMetaData = () => useAppSelector(selectGraphQueryResultMetaData); +export const useGraphQueryResultMeta: () => GraphMetadata = () => useAppSelector(selectGraphQueryResultMetaData); // Gives the schema export const useSchemaGraph: () => SchemaGraph = () => useAppSelector(schemaGraph); diff --git a/libs/shared/lib/mock-data/schema/moviesSchemaRaw.ts b/libs/shared/lib/mock-data/schema/moviesSchemaRaw.ts index d94c4a40cf94b9ee9f6d2c33b774d7029e0fa64d..5eb548312d4e03904bd1ac9151067a00fc4d4324 100644 --- a/libs/shared/lib/mock-data/schema/moviesSchemaRaw.ts +++ b/libs/shared/lib/mock-data/schema/moviesSchemaRaw.ts @@ -1,5 +1,5 @@ -import { SchemaUtils } from '@graphpolaris/shared/lib/schema/schema-utils'; import { SchemaFromBackend } from '../../schema'; +import { SchemaUtils } from '../../schema/schema-utils'; export const movieSchemaRaw: SchemaFromBackend = { nodes: [ diff --git a/libs/shared/lib/vis/common/types.ts b/libs/shared/lib/vis/common/types.ts index c8a62cefe77d001d0a66dd25ff4ebc513fabb544..37a3ea76ed809029f781c56787363b0e4bfe9b0b 100644 --- a/libs/shared/lib/vis/common/types.ts +++ b/libs/shared/lib/vis/common/types.ts @@ -5,7 +5,7 @@ import type { AppDispatch } from '../../data-access'; import { FC } from 'react'; import { Visualizations } from '../manager'; import { Edge, Node } from 'reactflow'; -import { GraphMetaData } from '../../data-access/statistics'; +import { GraphMetadata } from '../../data-access/statistics'; export type VisualizationConfiguration = { [id: string]: any }; @@ -22,7 +22,7 @@ export type VisualizationPropTypes = { ml: ML; configuration: VisualizationConfiguration; dispatch: AppDispatch; - graphMetadata: GraphMetaData; + graphMetadata: GraphMetadata; updateSettings: (newSettings: any) => void; handleHover: (val: any) => void; handleSelect: (val: any) => void; diff --git a/libs/shared/lib/vis/manager/VisualizationManager.tsx b/libs/shared/lib/vis/manager/VisualizationManager.tsx index 0654b769b904ebeae30a59d50c71d10d62b95dc4..8364e9446161e02ef95f0e9af245cf4147798f4a 100644 --- a/libs/shared/lib/vis/manager/VisualizationManager.tsx +++ b/libs/shared/lib/vis/manager/VisualizationManager.tsx @@ -17,7 +17,6 @@ import { } from '../../data-access'; import { VisualizationManagerType } from '.'; import { SelectType, addSelect } from '../../data-access/store/interactionSlice'; -import { MapComponent } from '../visualizations/mapvis/mapvis'; export const Visualizations: Record<string, Function> = { TableVis: () => import('../visualizations/tablevis/tablevis'), @@ -26,8 +25,7 @@ export const Visualizations: Record<string, Function> = { NodeLinkVis: () => import('../visualizations/nodelinkvis/nodelinkvis'), MatrixVis: () => import('../visualizations/matrixvis/matrixvis'), SemanticSubstratesVis: () => import('../visualizations/semanticsubstratesvis/semanticsubstratesvis'), - MapVis: () => Promise.resolve({ default: MapComponent }), - // MapVis: () => import('../visualizations/mapvis/mapvis'), + MapVis: () => import('../visualizations/mapvis/mapvis'), }; export const VISUALIZATION_TYPES: string[] = Object.keys(Visualizations); diff --git a/libs/shared/lib/vis/visualizations/mapvis/README.md b/libs/shared/lib/vis/visualizations/mapvis/README.md deleted file mode 100644 index 16346552bb0a3126070de930793fd23ab3305032..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Map visualization for Geospatial Graphs - -Uses Deck.gl and D3.js to visualize geospatial graphs on a map. The implementation uses OpenStreetMap as a basemap provider. - -## Folder notes - -- Archive: contains thesis work of Utrecht University on clustering for geospatial graphs -- Components: elements that are used in the interface -- Layers: layers are displayed on top of the base map and visualize the graph query result diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/FilterMenu.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/FilterMenu.tsx similarity index 94% rename from libs/shared/lib/vis/visualizations/mapvis/components/FilterMenu.tsx rename to libs/shared/lib/vis/visualizations/mapvis/archive/FilterMenu.tsx index e3db0635c0a57b7efd558ed312b81edfb6774776..60ca8ef89fe48b208dc2711231298d1ec5ac64ff 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/components/FilterMenu.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/archive/FilterMenu.tsx @@ -1,11 +1,10 @@ import React from 'react'; import { Close, ExpandLess, ExpandMore, PlayArrow } from '@mui/icons-material'; -import { GraphType } from '../types'; -import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; +import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; type Props = { setShowFilter: React.Dispatch<React.SetStateAction<boolean>>; - graphMetadata: GraphMetaData; + graphMetadata: GraphMetadata; }; const minDistance = 10; diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/LayerPanel.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/LayerPanel.tsx similarity index 97% rename from libs/shared/lib/vis/visualizations/mapvis/components/LayerPanel.tsx rename to libs/shared/lib/vis/visualizations/mapvis/archive/LayerPanel.tsx index 6a5d57b2c179674f587c3b70e74dfdeb9ca5062b..b68f7f4d18c42dcea37344a91f0f4e8a9662edae 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/components/LayerPanel.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/archive/LayerPanel.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { layerTypes } from '../layers'; -import { Layer } from '../types'; +import { layerTypes } from './layers'; +import { Layer } from '../mapvis.types'; import { makeLayer } from '../utlis'; type Props = { diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/SecondaryMenu.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/SecondaryMenu.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/components/SecondaryMenu.tsx rename to libs/shared/lib/vis/visualizations/mapvis/archive/SecondaryMenu.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/SelectedMenu.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/SelectedMenu.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/components/SelectedMenu.tsx rename to libs/shared/lib/vis/visualizations/mapvis/archive/SelectedMenu.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/NodeLinkMap.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/NodeLinkMap.tsx deleted file mode 100644 index aa0f715bf2b0ab5cc533e56483ca7342a4fb9ec1..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/NodeLinkMap.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import React, { useState } from 'react'; -import { useGraphQueryResult } from '../../../../../data-access'; -import { MapView, PickingInfo } from '@deck.gl/core/typed'; -import DeckGL from '@deck.gl/react/typed'; -import { getTooltip } from './Tooltip'; -import * as d3 from 'd3'; -import { createGraph } from './core'; -import { generateBaseLayer } from './layers'; - -type Props = { - mapType: number; - Layer: any; - nodelink: string; - isAggregated: boolean; - isFanned: boolean; - brushing: boolean; -}; - -export const NodeLinkMap = ({ mapType = 0, nodelink, Layer }: Props) => { - const [brushing, setBrushing] = useState<boolean>(false); - const [clusterRadius, setClusterRadius] = useState<number>(40); - const [viewport, setViewport] = useState<{ [key: string]: number }>({ - longitude: -102.4, - latitude: 37.8, - zoom: 0, - }); - - const graphQueryResult = useGraphQueryResult(); - - const graph = React.useMemo(() => { - return createGraph(graphQueryResult.nodes, graphQueryResult.edges); - }, [graphQueryResult]); - - React.useEffect(() => { - setViewport({ - ...graph.calculateCenterPoint(), - zoom: 0, - }); - }, [graph]); - - const colorScale: d3.ScaleLinear<string, string> = d3.scaleLinear<string>().domain([0, 6000]).range(['blue', 'red']); - - const layers = [ - generateBaseLayer(mapType), - new Layer({ - graph: graph, - colorScale: colorScale, - brushing: brushing, - clusterRadius: clusterRadius, - }), - ]; - - const renderTooltip = (info: PickingInfo) => { - const { object } = info; - if (!brushing && object) { - return getTooltip({ object }); - } - return undefined; - }; - - if (!graphQueryResult) return <h1>No data</h1>; - - return ( - <div> - {graph && ( - <DeckGL - layers={layers} - views={new MapView({ repeat: true, controller: true })} - initialViewState={viewport} - getTooltip={({ object }) => object && renderTooltip(object)} - onViewStateChange={({ viewState }) => setViewport(viewState)} - /> - )} - <div - style={{ - display: 'flex', - flexDirection: 'column', - position: 'absolute', - top: '20px', - right: '20px', - backgroundColor: 'white', - padding: '30px', - borderRadius: '4px', - boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.2)', - }} - > - <h3>Control Panel</h3> - {nodelink === 'cluster' && ( - <label> - Cluster radius - <input - name="Cluster radius" - type="range" - min="10" - max="100" - value={clusterRadius} - onChange={(e) => setClusterRadius(parseInt(e.target.value))} - /> - </label> - )} - <label> - Brushing - <input name="Brushing" type="checkbox" checked={brushing} onChange={(e) => setBrushing(e.target.checked)} /> - </label> - </div> - </div> - ); -}; - -export default NodeLinkMap; diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/Tooltip.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/Tooltip.tsx deleted file mode 100644 index 081096322c6af3c41e54574234ee99fb81466587..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/Tooltip.tsx +++ /dev/null @@ -1,92 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -const formatData = (data: { [key: string]: any }) => { - let result = ''; - - for (const key in data) { - if (typeof data[key] === 'object') { - result += `${key}:<br>${formatData(data[key])}`; - } else { - result += `${key}: ${data[key]}<br>`; - } - } - - return result; -}; - -export const getTooltip = (object: any) => { - if (object && object.isEdge) { - const { from, to } = object; - const attributes = formatData(object); - return { - html: ` - <div> - <h3>${from}-${to}</h3> - <p>${attributes}</p> - </div> - `, - }; - } else if (object && object.isNode && object.data.hasOwnProperty('locations')) { - const { id, lon, lat, data } = object; - return { - html: ` - <div> - <h3>Id: ${id}</h3> - <p>Location: ${lon.toFixed(2)}-${lat.toFixed(2)}</p> - <p>City: ${data.locations.slice(0, 5)}...</p> - </div> - `, - }; - } else if (object && object.hasOwnProperty('properties') && object.properties.cluster) { - const { id } = object; - const attributes = formatData(object); - return { - html: ` - <div> - <h3>Id: ${id}</h3> - <p>${attributes}</p> - </div> - `, - }; - } else if (object && object.hasOwnProperty('geometry')) { - const { geometry, properties } = object; - return { - html: ` - <div> - <h3>Airport: ${properties.id}</h3> - <p>City: ${properties.data.city}</p> - </div> - `, - }; - } else if (object && object.hasOwnProperty('id') && object.data.hasOwnProperty('prediction')) { - const { from, to, count, data } = object; - const test = formatData(data); - return { - html: ` - <div> - <h3>${from}-${to}</h3> - <p>Flight count: ${count}</p> - <p>Predicted passengers: ${data.prediction}</p> - </div> - `, - }; - } else if (object && object.isNode) { - const { id, data } = object; - const attributes = formatData(data); - return { - html: ` - <div> - <h3>Id: ${id}</h3> - <p>${attributes}</p> - - </div> - `, - }; - } else { - return null; - } -}; diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/NodeLinkViewModel.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/NodeLinkViewModel.tsx deleted file mode 100644 index 8500cb6f0d998e0f5f1166bda809bef133a636eb..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/NodeLinkViewModel.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { ViewModel, Command, GeoEdgeType, GeoGraphType } from '../types'; - -export class NodeLinkViewModel implements ViewModel { - data: GeoGraphType; - private original_data: GeoGraphType; - - constructor(data: GeoGraphType) { - this.data = data; - this.addCoordinatesToEdges(); - this.original_data = this.data; - } - - executeCommand(command: Command) { - this.data = command.execute(this.original_data); - } - - addCoordinatesToEdges() { - // Add coordinates to edges based on nodes - const edges: Record<string, any> = {}; - - this.data.edges.forEach((edge: GeoEdgeType) => { - const key = `${edge.from}_${edge.to}`; - if (!edges[key]) { - edges[key] = { - ...edge, - from_coordinates: this.data.nodes.find((node) => node.id === edge.from)?.attributes.coordinates, - to_coordinates: this.data.nodes.find((node) => node.id === edge.to)?.attributes.coordinates, - count: 1, - }; - } else { - edges[key].count++; - } - }); - - this.data = { - nodes: this.data.nodes, - edges: Object.values(edges), - }; - } - - resetGraph() { - this.data = this.original_data; - } - - getEdges() { - return this.data.edges; - } - - getNodes() { - return this.data.nodes; - } -} diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/aggregateCommand.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/aggregateCommand.tsx deleted file mode 100644 index 6ba8c3502f83a73526ff0b321862b09ccaa406c4..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/aggregateCommand.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { GeoNodeType, Command, GeoEdgeType, GeoGraphType, Coordinates } from '../types'; -import { calcAverageCoordinates } from '../utils'; -import * as d3 from 'd3'; - -export class AggregateCommand implements Command { - /** - * Groups nodes by a provided attribute - * Computes average coordinates for new grouped node - * Updates edge coordinates accordingly - */ - private dimension: string; - - constructor(dimension: string) { - this.dimension = dimension; - } - - execute(data: GeoGraphType) { - const graph: GeoGraphType = { ...data }; - graph.nodes = aggregateNodes(graph, this.dimension); - graph.edges = aggregateEdges(graph); - return graph; - } -} - -const aggregateNodes = (graph: GeoGraphType, dimension: string): GeoNodeType[] => { - const nodesByAttribute: Map<string, GeoNodeType[]> = d3.group(graph.nodes, (node: GeoNodeType) => eval(`node.${dimension}`)); - - const bundleByAttribute: GeoNodeType[] = []; - - nodesByAttribute.forEach((group) => { - const lat: number[] = []; - const long: number[] = []; - const locations: string[] = []; - - group.forEach((location: GeoNodeType) => { - const coordinates: Coordinates = location.attributes.coordinates; - lat.push(coordinates[0]); - long.push(coordinates[1]); - locations.push(location.id); - }); - - const average: [number, number] = calcAverageCoordinates(lat, long); - const name: string = eval(`group[0].${dimension}`); - - bundleByAttribute.push({ - id: name, - attributes: { - coordinates: average, - locations: locations, - }, - }); - }); - - return bundleByAttribute; -}; - -const aggregateEdges = (data: GeoGraphType): GeoEdgeType[] => { - const locations: { [key: string]: string[] } = data.nodes.reduce((locations: any, node) => { - locations[node.id] = node.attributes.locations; - return locations; - }, {}); - - const bundles: GeoEdgeType[] = []; - - data.edges.forEach((edge: GeoEdgeType) => { - const from_location: string | undefined = Object.keys(locations).find((state) => locations[state].includes(edge.from)); - - const to_location: string | undefined = Object.keys(locations).find((state) => locations[state].includes(edge.to)); - - if (from_location && to_location) { - if (from_location === to_location) return; // edge is within group - - const from_coordinates: Coordinates | undefined = data.nodes.find((node: GeoNodeType) => node.id === from_location)?.attributes - .coordinates; - - const to_coordinates: Coordinates | undefined = data.nodes.find((node: GeoNodeType) => node.id === to_location)?.attributes - .coordinates; - - const newEdge: GeoEdgeType = { - ...edge, - from: from_location, - to: to_location, - from_coordinates: from_coordinates, - to_coordinates: to_coordinates, - }; - - bundles.push(newEdge); - } - }); - - return bundles; -}; diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/unfoldingEdges.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/unfoldingEdges.tsx deleted file mode 100644 index 9c3471c2bc80c1afe0a2b41f1166cc4a1c01379e..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/unfoldingEdges.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { GeoEdgeType, GeoGraphType, Coordinates } from '../types'; -import { calcNodeDistance } from '../utils'; -import * as d3 from 'd3'; - -export const fanEdges = (graph: GeoGraphType, selectedNode: string | null, maxWidth: number) => { - const edgeGroups: { [key: string]: GeoEdgeType[] } = {}; - - graph.data.edges.forEach((edge: GeoEdgeType) => { - const key: string = `${edge.from}-${edge.to}`; - if (!edgeGroups[key]) { - edgeGroups[key] = [edge]; - } else { - edgeGroups[key].push(edge); - } - }); - - const fanned: GeoEdgeType[] = []; - - const colorScale: d3.ScaleLinear<string, string> = d3.scaleLinear<string>().domain([0, 2000]).range(['yellow', 'red']); - - Object.values(edgeGroups).forEach((bundle) => { - let adjacentNodeSelected: boolean = false; - if ((selectedNode !== null && selectedNode === bundle[0].from) || selectedNode === bundle[0].to) { - adjacentNodeSelected = true; - } - - if (selectedNode && !adjacentNodeSelected) { - return; - } - - const prediction: number = bundle[0].attributes.prediction; - const color: string = colorScale(prediction); - const rgb: string[] = color.replace(/[^\d,]/g, '').split(','); - const num_edges: number = bundle.length; - - if (bundle.length === 1) { - fanned.push({ - ...bundle[0], - from_coordinates: bundle[0].from_coordinates, - to_coordinates: bundle[0].to_coordinates, - color: rgb.map(Number), - numEdges: num_edges, - }); - } else { - const source: Coordinates = bundle[0].from_coordinates; - const target: Coordinates = bundle[0].to_coordinates; - const node_distance: number = calcNodeDistance(source, target); - const inViewport: boolean = node_distance < maxWidth - 20; - - bundle.forEach((edge: GeoEdgeType, index: number) => { - const newEdge: GeoEdgeType = { - ...edge, - from_coordinates: source, - to_coordinates: target, - color: rgb.map(Number), - numEdges: num_edges, - }; - - if (inViewport) { - fanned.push(newEdge); - } else { - fanned.push({ - ...newEdge, - tilt: (index / num_edges) * Math.PI, - numEdges: 1, - }); - } - }); - } - }); - - return fanned; -}; diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/viewModel.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/viewModel.tsx deleted file mode 100644 index 149198fde45102a7fff1c11a95bcc4fe15dc5d6c..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/archive/viewModel.tsx +++ /dev/null @@ -1,58 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { ViewModel, Command, GeoEdgeType, GeoGraphType } from '../types'; - -export class NodeLinkViewModel implements ViewModel { - data: GeoGraphType; - private original_data: GeoGraphType; - - constructor(data: any) { - this.data = data; - this.addCoordinatesToEdges(); - this.original_data = this.data; - } - - executeCommand(command: Command) { - this.data = command.execute(this.original_data); - } - - addCoordinatesToEdges() { - // Add coordinates to edges based on nodes - const edges: { [key: string]: GeoEdgeType } = {}; - - this.data.edges.forEach((edge: GeoEdgeType) => { - const key = `${edge.from}_${edge.to}`; - if (!edges[key]) { - edges[key] = { - ...edge, - from_coordinates: this.data.nodes.find((node) => node.id === edge.from)?.attributes.coordinates, - to_coordinates: this.data.nodes.find((node) => node.id === edge.to)?.attributes.coordinates, - count: 1, - }; - } else { - edges[key].count++; - } - }); - - this.data = { - nodes: this.data.nodes, - edges: Object.values(edges), - }; - } - - resetGraph() { - this.data = this.original_data; - } - - getEdges() { - return this.data.edges; - } - - getNodes() { - return this.data.nodes; - } -} diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/config.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/config.tsx deleted file mode 100644 index f8e517a470a295d91dc90bbbbd9781dd159c6d06..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/config.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -interface MapTypes { - [key: string]: string[]; -} - -export const mapTypes: MapTypes = { - // Urls for the base map - general: [ - 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png', - 'https://b.tile.openstreetmap.org/{z}/{x}/{y}.png', - 'https://c.tile.openstreetmap.org/{z}/{x}/{y}.png', - ], - cycle: [ - 'https://a.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', - 'https://b.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', - 'https://c.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', - ], - stamenToner: [ - 'https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png', - 'https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png', - 'https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png', - ], - stamenTerrain: [ - 'https://stamen-tiles.a.ssl.fastly.net/terrain/{z}/{x}/{y}.png', - 'https://stamen-tiles.a.ssl.fastly.net/terrain/{z}/{x}/{y}.png', - 'https://stamen-tiles.a.ssl.fastly.net/terrain/{z}/{x}/{y}.png', - ], - stamenWaterColor: [ - 'https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png', - 'https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png', - 'https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png', - ], -}; diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/attributeFaceting.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/attributeFaceting.tsx deleted file mode 100644 index 8a9a539883383c1175df687b211d5190ee78a9fe..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/attributeFaceting.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import * as d3 from 'd3'; -import GeoNode from './node'; -import GeoEdge from './edge'; -import { calcAverageCoordinates } from '../utils'; - -export const facetNodes = (nodes: GeoNode[], dimension: string) => { - const nodesByAttribute: Map<string, GeoNode[]> = d3.group(nodes, (node: GeoNode) => eval(`node.data.${dimension}`)); - - const bundleByAttribute: GeoNode[] = []; - - nodesByAttribute.forEach((group) => { - const lat: number[] = []; - const long: number[] = []; - const locations: any[] = []; - - group.forEach((node: GeoNode) => { - const coordinates: number[] = node.getCoordinates(); - lat.push(coordinates[0]); - long.push(coordinates[1]); - locations.push(node.id); - }); - - const average: [number, number] = calcAverageCoordinates(lat, long); - const name: string = eval(`group[0].data.${dimension}`); - - bundleByAttribute.push( - new GeoNode({ - id: name, - lon: average[0], - lat: average[1], - data: { - locations: locations, - }, - }) - ); - }); - - return bundleByAttribute; -}; - -export const updateEdges = (nodes: GeoNode[], edges: GeoEdge[]) => { - const locations: { [key: string]: string[] } = nodes.reduce((locations: { [key: string]: string[] }, node: GeoNode) => { - locations[node.id] = node.data.locations; - return locations; - }, {}); - - const bundles: { [key: string]: GeoEdge } = {}; - - edges.forEach((edge: GeoEdge) => { - const from_location: string | undefined = Object.keys(locations).find((state) => locations[state].includes(edge.from as string)); - - const to_location: string | undefined = Object.keys(locations).find((state) => locations[state].includes(edge.to as string)); - - if (from_location && to_location) { - if (from_location === to_location) return; // edge is within group - - const from: number[] | undefined = nodes.find((node: GeoNode) => node.id === from_location)?.getCoordinates(); - const to: number[] | undefined = nodes.find((node: GeoNode) => node.id === to_location)?.getCoordinates(); - - const key = `${from_location}-${to_location}`; - if (!(key in bundles)) { - edge.source = from ? [from[0], from[1]] : null; - edge.target = to ? [to[0], to[1]] : null; - bundles[key] = edge; - } else { - bundles[key].count += 1; - } - } - }); - - return Object.values(bundles); -}; diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/edge.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/edge.tsx deleted file mode 100644 index 36fd603c0df6103717e30a6f82b1b7b24e2b54dd..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/edge.tsx +++ /dev/null @@ -1,38 +0,0 @@ -export default class GeoEdge { - public id: string | number; - public from: string | number; - public to: string | number; - public source: [number, number] | null; - public target: [number, number] | null; - public directed: boolean; - public data: { [key: string]: any }; - public count: number; - public isEdge: boolean; - - constructor({ - id, - from, - to, - directed = false, - data = {}, - }: { - id: string | number; - from: string | number; - to: string | number; - directed?: boolean; - data?: { [key: string]: any }; - }) { - this.id = id; - this.from = from; - this.to = to; - this.source = null; - this.target = null; - this.directed = directed; - this.data = data; - this.count = 1; - this.isEdge = true; - } - - getSourceNodeId = (): string | number => this.from; - getTargetNodeId = (): string | number => this.to; -} diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/graph.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/graph.tsx deleted file mode 100644 index 09b92528fe127b887c30e8cf18f15ed6abea6ce4..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/graph.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { facetNodes, updateEdges } from './attributeFaceting'; -import GeoEdge from './edge'; -import GeoNode from './node'; - -export default class GeoGraph { - public nodeMap: Record<string, GeoNode>; - public edgeMap: Record<string, GeoEdge>; - public cache = {}; - - constructor() { - this.nodeMap = {}; - this.edgeMap = {}; - - this.cache = { - nodes: {}, - edges: {}, - }; - } - - addNode(node: GeoNode) { - this.nodeMap[node.id] = node; - } - - findNode(nodeID: string | number) { - return this.nodeMap[nodeID]; - } - - getNodes = () => Object.values(this.nodeMap); - - batchAddNodes(nodes: GeoNode[]) { - nodes.forEach((node) => this.addNode(node)); - this.updateCache(); - } - - addEdge(edge: GeoEdge) { - const sourceNode: GeoNode = this.findNode(edge.getSourceNodeId()); - const targetNode: GeoNode = this.findNode(edge.getTargetNodeId()); - if (!sourceNode || !targetNode) return; - const key = `${sourceNode.id}-${targetNode.id}`; - if (!this.edgeMap[key]) { - edge.source = sourceNode.getCoordinates(); - edge.target = targetNode.getCoordinates(); - this.edgeMap[key] = edge; - } else { - this.edgeMap[key].count++; - } - } - - getEdges = () => Object.values(this.edgeMap); - - batchAddEdges(edges: GeoEdge[]) { - edges.forEach((edge) => this.addEdge(edge)); - this.updateCache(); - } - - groupBy(dimension: string) { - const nodes = facetNodes(Object.values(this.nodeMap), dimension); - const edges = updateEdges(nodes, Object.values(this.edgeMap)); - return { - nodes: nodes, - edges: edges, - }; - } - - updateCache() { - this.cache = { - nodes: this.nodeMap, - edges: this.edgeMap, - }; - } - - calculateCenterPoint() { - const nodes: GeoNode[] = Object.values(this.nodeMap); - const totalNodes: number = nodes.length; - let centerLon: number = 0; - let centerLat: number = 0; - - for (const node of nodes) { - const coordinates: number[] = node.getCoordinates(); - centerLon += coordinates[0]; - centerLat += coordinates[1]; - } - - return { - longitude: centerLon / totalNodes, - latitude: centerLat / totalNodes, - }; - } -} diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/index.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/index.tsx deleted file mode 100644 index b536820e6e7acf20ba0a367bc74508e44c38ee02..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/index.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import GeoGraph from './graph'; -import GeoNode from './node'; -import GeoEdge from './edge'; - -export const createGraph = (nodes: { [key: string]: any }, edges: { [key: string]: any }): GeoGraph => { - const graph = new GeoGraph(); - - const glNodes: GeoNode[] = nodes.map((node: any) => { - const { id, attributes } = node; - return new GeoNode({ - id, - lon: attributes.coordinates[0], - lat: attributes.coordinates[1], - data: attributes, - }); - }); - graph.batchAddNodes(glNodes); - - const glEdges: GeoEdge[] = edges.map((edge: any) => { - const { id, from, to, directed, attributes } = edge; - return new GeoEdge({ - id, - from, - to, - directed, - data: attributes, - }); - }); - graph.batchAddEdges(glEdges); - - return graph; -}; diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/node.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/node.tsx deleted file mode 100644 index 087cf03bfdd2ec052ccf96f329322dd23cd69c55..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/core/node.tsx +++ /dev/null @@ -1,17 +0,0 @@ -export default class GeoNode { - public id: string | number; - public lon: number; - public lat: number; - public data: { [key: string]: any }; - public isNode: boolean; - - constructor({ id, lon, lat, data = {} }: { id: string | number; lon: number; lat: number; data?: { [key: string]: any } }) { - this.id = id; - this.lon = lon; - this.lat = lat; - this.data = data; - this.isNode = true; - } - - getCoordinates = (): [number, number] => [this.lon, this.lat]; -} diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoBaseLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoBaseLayer.tsx deleted file mode 100644 index d4fa1dfa47f633f65c03146e13d539d83c3c4c4f..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoBaseLayer.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { TileLayer, BitmapLayer } from 'deck.gl/typed'; -import { mapTypes } from '../config'; - -export const generateBaseLayer = (mapType: number) => { - /** - * Generates base layer on which the node-link will be visualized - * Uses deck gl layers in combination with the openstreetmap api - * Inspiration from: https://github.com/visgl/deck.gl/tree/8.9-release/examples/website/map-tile - */ - - const urls: string[] = mapTypes[Object.keys(mapTypes)[mapType]]; - - return new TileLayer({ - data: urls, - minZoom: 0, - maxZoom: 19, - tileSize: 256, - - //@ts-ignore - renderSubLayers: (props: any) => { - const { - bbox: { west, south, east, north }, - } = props.tile; - - return new BitmapLayer(props, { - data: undefined, - image: props.data, - bounds: [west, south, east, north], - }); - }, - }); -}; diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoClusterLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoClusterLayer.tsx deleted file mode 100644 index be778fbf8be0acf28850521a94891333f927047a..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoClusterLayer.tsx +++ /dev/null @@ -1,135 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { CompositeLayer } from 'deck.gl/typed'; -import Supercluster from 'supercluster'; -import { ScatterplotLayer, LineLayer } from '@deck.gl/layers/typed'; -import { LayerProps } from '../types'; -import { getColorFromScale } from '../utils'; -import { BrushingExtension } from '@deck.gl/extensions/typed'; -import GeoEdge from '../core/edge'; -import GeoNode from '../core/node'; - -export class GeoClusterLayer extends CompositeLayer<LayerProps> { - /** - * Layer that clusters the given graph - * Cluster layer uses supercluster library to cluster nodes - * Edges get updated accordingly - */ - static componentName = 'Cluster Layer'; - - shouldUpdateState({ changeFlags, props, oldProps }: { props: LayerProps; oldProps: LayerProps; changeFlags: any }) { - return changeFlags.somethingChanged || oldProps.clusterRadius !== props.clusterRadius; - } - - updateState({ props, oldProps, changeFlags }: { props: LayerProps; oldProps: LayerProps; changeFlags: any }) { - const rebuildIndex = changeFlags.dataChanged; - const zoom = Math.floor(this.context.viewport.zoom); - const radiusChanged = oldProps.clusterRadius !== props.clusterRadius; - - if (rebuildIndex || radiusChanged) { - const index = new Supercluster({ - radius: props.clusterRadius, - maxZoom: 16, - }); - index.load( - props.graph.getNodes().map((node: GeoNode) => ({ - geometry: { coordinates: node.getCoordinates() }, - properties: node, - })) - ); - this.setState({ index }); - } - - if (rebuildIndex || zoom !== this.state.zoom) { - const nodes = this.state.index.getClusters([-180, -85, 180, 85], zoom); - - // Create node index for faster coordinate search - const node_index: { [key: string]: any } = {}; - for (const node of nodes) { - const coordinates = node.geometry.coordinates; - if (!node.properties.cluster) { - node_index[node.properties.id] = { - id: node.properties.id, - coordinates: coordinates, - }; - } else { - const children = this.state.index.getLeaves(node.id, Infinity); - for (const child of children) { - if (!node_index[child.properties.id]) { - node_index[child.properties.id] = { - id: node.id, - coordinates: coordinates, - }; - } - } - } - } - - // Add updated coordinates to edges - const edges: { [key: string]: GeoEdge } = {}; - props.graph.getEdges().forEach((edge: GeoEdge) => { - const source: { [key: string]: any } = node_index[edge.from]; - const target: { [key: string]: any } = node_index[edge.to]; - - if (source && target) { - if (source.coordinates[0] === target.coordinates[0] && source.coordinates[1] === target.coordinates[1]) { - return; - } else { - const key = `${source.id}-${target.id}`; - if (!edges[key]) { - edges[key] = { - ...edge, - source: source.coordinates, - target: target.coordinates, - count: 1, - }; - } else edges[key].count++; - } - } - }, []); - - this.setState({ - nodes, - edges: Object.values(edges), - zoom, - }); - } - } - - renderLayers() { - return [ - new LineLayer({ - id: 'edges', - data: this.state.edges, - pickable: true, - //@ts-ignore - extensions: [new BrushingExtension()], - brushingEnabled: this.props.brushing, - brushingRadius: 500000, - brushingTarget: 'source_target', - getWidth: (d: any) => Math.sqrt(d.count) * 0.3, - getSourcePosition: (d: any) => d.source, - getTargetPosition: (d: any) => d.target, - getColor: (d: any) => getColorFromScale(d.data.prediction, this.props.colorScale), - }), - new ScatterplotLayer({ - id: 'nodes', - data: this.state.nodes, - opacity: 1, - pickable: true, - interactive: true, - filled: true, - radiusScale: 3, - radiusMinPixels: 5, - radiusMaxPixels: 20, - getRadius: (d: any) => (d.hasOwnProperty('type') ? d.properties.point_count * 1000 : 10), - getFillColor: (d: any) => (d.properties.cluster ? [250, 0, 0] : [0, 0, 255]), - getPosition: (d: any) => d.geometry.coordinates, - }), - ]; - } -} diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoFacetingLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoFacetingLayer.tsx deleted file mode 100644 index 2747d3282f8cabaa4db7e1db1dc14518d1258a9b..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoFacetingLayer.tsx +++ /dev/null @@ -1,112 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { CompositeLayer } from 'deck.gl/typed'; -import { ScatterplotLayer, LineLayer } from '@deck.gl/layers/typed'; -import { LayerProps } from '../types'; -import { getColorFromScale } from '../utils'; -import { BrushingExtension } from '@deck.gl/extensions/typed'; - -export class GeoFacetingLayer extends CompositeLayer<LayerProps> { - /** - * Faceting layer implements attribute driven faceting - * Main drawback is the predefined attributes (which not every data set has) - * Also, the data has to be duplicated in order to be able to go back to initial view - */ - static componentName = 'FacetingLayer Layer'; - - shouldUpdateState({ changeFlags }: { changeFlags: any }) { - return changeFlags.somethingChanged; - } - - updateState({ props, changeFlags }: { props: LayerProps; changeFlags: any }) { - const rebuildIndex = changeFlags.dataChanged; - const zoom = Math.floor(this.context.viewport.zoom); - - if (rebuildIndex || zoom !== this.state.zoom) { - let nodes = []; - let edges = []; - - // Aggregate based on predefined levels - if (zoom >= 5) { - nodes = props.graph.getNodes(); - edges = props.graph.getEdges(); - } else if (zoom <= 4 && zoom >= 3) { - const graphData = props.graph.groupBy('region'); - nodes = graphData.nodes; - edges = graphData.edges; - } else if (zoom === 2) { - const graphData = props.graph.groupBy('country'); - nodes = graphData.nodes; - edges = graphData.edges; - } else if (zoom <= 1) { - const graphData = props.graph.groupBy('continent'); - nodes = graphData.nodes; - edges = graphData.edges; - } - - const node_index: { [key: string]: any } = {}; - for (const node of nodes) { - const coordinates = node.getCoordinates(); - if (!node.data.locations) { - node_index[node.id] = { - id: node.id, - coordinates: coordinates, - }; - } else { - const children = node.data.locations; - for (const child of children) { - if (!node_index[child]) { - node_index[child] = { - id: node.id, - coordinates: coordinates, - }; - } - } - } - } - - this.setState({ - nodes: nodes, - edges: edges, - zoom: zoom, - }); - } - } - - renderLayers() { - return [ - new LineLayer({ - id: 'edges', - data: this.state.edges, - pickable: true, - //@ts-ignore - extensions: [new BrushingExtension()], - brushingEnabled: this.props.brushing, - brushingRadius: 500000, - brushingTarget: 'source_target', - getWidth: (d: any) => Math.sqrt(d.count) * 0.3, - getSourcePosition: (d: any) => d.source, - getTargetPosition: (d: any) => d.target, - getColor: (d: any) => getColorFromScale(d.data.prediction, this.props.colorScale), - }), - new ScatterplotLayer({ - id: 'nodes', - data: this.state.nodes, - pickable: true, - opacity: 0.6, - interactive: true, - filled: true, - radiusScale: 3, - radiusMinPixels: 3, - radiusMaxPixels: 20, - getRadius: (d: any) => (d.data.hasOwnProperty('locations') ? d.data.locations.length * 1000 : 10), - getPosition: (d: any) => d.getCoordinates(), - getFillColor: [250, 0, 0], - }), - ]; - } -} diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoNodeLinkLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoNodeLinkLayer.tsx deleted file mode 100644 index 9ff84e8183c706d18a1ca3714d76c2ca12ab9f4c..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/GeoNodeLinkLayer.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { CompositeLayer } from 'deck.gl/typed'; -import { ScatterplotLayer, LineLayer } from '@deck.gl/layers/typed'; -import { LayerProps } from '../types'; -import { getColorFromScale } from '../utils'; -import { BrushingExtension } from '@deck.gl/extensions/typed'; -import GeoNode from '../core/node'; - -export class GeoNodeLinkLayer extends CompositeLayer<LayerProps> { - /** - * Basic node-link layer that just visualizes the data - */ - static componentName = 'Knowledge Graph Layer'; - - shouldUpdateState({ changeFlags }: { changeFlags: any }) { - return changeFlags.somethingChanged; - } - - updateState({ props }: { props: LayerProps }) { - const zoom = Math.floor(this.context.viewport.zoom); - if (zoom !== this.state.zoom) { - const node_index: { [key: string]: any } = props.graph.getNodes().reduce((obj: { [key: string]: any }, item: GeoNode) => { - obj[item.id] = { coordinates: [item.lon, item.lat] }; - return obj; - }, {}); - - this.setState(zoom); - } - } - - renderLayers() { - const { graph, colorScale, brushing } = this.props; - - return [ - new LineLayer({ - id: 'edges', - data: graph.getEdges(), - pickable: true, - //@ts-ignore - extensions: [new BrushingExtension()], - brushingEnabled: brushing, - brushingRadius: 500000, - brushingTarget: 'source_target', - getWidth: (d: any) => d.count, - getSourcePosition: (d: any) => d.source, - getTargetPosition: (d: any) => d.target, - getColor: (d: any) => getColorFromScale(d.data.prediction, colorScale), - }), - new ScatterplotLayer({ - id: 'nodes', - data: graph.getNodes(), - pickable: true, - opacity: 0.6, - interactive: true, - filled: true, - radiusScale: 3, - radiusMinPixels: 3, - radiusMaxPixels: 20, - getPosition: (d: any) => d.getCoordinates(), - getFillColor: [250, 0, 0], - }), - ]; - } -} diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/index.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/index.tsx deleted file mode 100644 index e0dbe49cdfb5cb704222e8faa039cbaa7d7e28b3..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/layers/index.tsx +++ /dev/null @@ -1,4 +0,0 @@ -export { GeoNodeLinkLayer } from './GeoNodeLinkLayer'; -export { GeoClusterLayer } from './GeoClusterLayer'; -export { GeoFacetingLayer } from './GeoFacetingLayer'; -export { generateBaseLayer } from './GeoBaseLayer'; diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/readability.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/readability.tsx deleted file mode 100644 index 0512d072441cf58a319ed1f976356f4d537c3a90..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/readability.tsx +++ /dev/null @@ -1,138 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { GeoNodeType, GeoEdgeType, Coordinates } from './types'; -import GeoGraph from './core/graph'; - -export const evaluateGraph = (graph: GeoGraph, edges: GeoEdgeType[], nodes: GeoNodeType[], node_index: { [key: string]: GeoNodeType }) => { - try { - if (!graph || !edges) throw new Error('Not the right input'); - const nEdges = edges.length; - const nNodes = nodes.length; - const edgeMetrics = calcEdgeMetrics(edges); - return { - nEdges: nEdges, - nNodes: nNodes, - edgeCrossings: edgeMetrics.crossings, - totalEdgeLength: edgeMetrics.edgeLength, - averageNodeOffset: nodeOffset(graph, node_index), - }; - } catch (error) { - console.log('Failed evaluating the graph', error); - return null; - } -}; - -const nodeOffset = (graph: GeoGraph, node_index: { [key: string]: GeoNodeType }) => { - /** - * Calculates the average change in position (offset) of nodes - * Assess reliability of a map - * Inspired by Behrish et al paper about Quality metrics for InfoVis - */ - - let totalOffset = 0; - if (node_index) { - for (const node in node_index) { - const location = node_index[node].coordinates; - const original = graph.findNode(node).getCoordinates(); - const distance = calcDistance(location, original); - totalOffset += distance; - } - } - return Number((totalOffset / graph.getNodes().length).toFixed(2)); -}; - -const calcDistance = (location_1: Coordinates, location_2: Coordinates) => { - return Math.hypot(location_1[0] - location_2[0], location_1[1] - location_2[1]); -}; - -export const calcEdgeMetrics = (edges: GeoEdgeType[]) => { - /** - * Custom implementation from greadability.js (https://github.com/rpgove/greadability/blob/master/greadability.js) - * Improved it with a sweep line algorithm - */ - let n_crossings = 0; - let totalLength = 0; - - edges.sort((a, b) => a.source[0] - b.source[0]); - - const activeSegments: GeoEdgeType[] = []; - - for (let i = 0; i < edges.length; i++) { - const edge: GeoEdgeType = edges[i]; - - // Calc edge length - const edgeLength = calcDistance(edge.source, edge.target); - totalLength += Math.abs(edgeLength); - - // Calc edge crossings - let count = 0; - while (count < activeSegments.length) { - const segment: GeoEdgeType = activeSegments[count]; - if (segment.source[0] <= edge.target[0]) { - activeSegments.splice(count, 1); - } else { - if (doEdgesCross(edge, segment)) { - n_crossings++; - } - count++; - } - } - activeSegments.push(edge); - } - return { - crossings: n_crossings, - edgeLength: Math.round(totalLength), - }; -}; - -const doEdgesCross = (edge_one: GeoEdgeType, edge_two: GeoEdgeType) => { - const from1 = edge_one.source; - const to1 = edge_one.target; - const from2 = edge_two.source; - const to2 = edge_two.target; - - return linesCross([from1, to1], [from2, to2]); -}; - -const direction = (pi: Coordinates, pj: Coordinates, pk: Coordinates) => { - var p1 = [pk[0] - pi[0], pk[1] - pi[1]]; - var p2 = [pj[0] - pi[0], pj[1] - pi[1]]; - var product = p1[0] * p2[1] - p2[0] * p1[1]; - if (product === 0) { - return 0; - } - return product; -}; - -const onSegment = (pi: Coordinates, pj: Coordinates, pk: Coordinates) => { - return ( - Math.min(pi[0], pj[0]) <= pk[0] && pk[0] <= Math.max(pi[0], pj[0]) && Math.min(pi[1], pj[1]) <= pk[1] && pk[1] <= Math.max(pi[1], pj[1]) - ); -}; - -const linesCross = (line1: Coordinates[], line2: Coordinates[]) => { - var d1, d2, d3, d4; - - d1 = direction(line2[0], line2[1], line1[0]); - d2 = direction(line2[0], line2[1], line1[1]); - d3 = direction(line1[0], line1[1], line2[0]); - d4 = direction(line1[0], line1[1], line2[1]); - - if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) { - return true; - } else if (d1 === 0 && onSegment(line2[0], line2[1], line1[0])) { - return true; - } else if (d2 === 0 && onSegment(line2[0], line2[1], line1[1])) { - return true; - } else if (d3 === 0 && onSegment(line1[0], line1[1], line2[0])) { - return true; - } else if (d4 === 0 && onSegment(line1[0], line1[1], line2[1])) { - return true; - } - - return false; -}; diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/types.ts b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/types.ts index 8ed19978c323a1b073056f3264df19922bb9009e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/types.ts +++ b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/types.ts @@ -1,99 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -export type Coordinates = [number, number]; - -export interface GeoNodeType { - id: string; - [key: string]: any; - attributes: { - coordinates: Coordinates; - [key: string]: any; - }; -} - -export interface GeoEdgeType { - id: string; - from: string; - to: string; - [key: string]: any; - attributes: { - [key: string]: any; - }; -} - -export interface Node { - id: string; - [key: string]: any; - attributes: { - coordinates: Coordinates; - [key: string]: any; - }; -} - -export interface Edge { - id: string; - from: string; - to: string; - [key: string]: any; - attributes: { - [key: string]: any; - }; -} - -export interface GeoGraphType { - nodes: GeoNodeType[]; - edges: GeoEdgeType[]; - [key: string]: any; -} - -export interface Command { - execute: (data: GeoGraphType) => GeoGraphType; -} - -export interface ViewModel { - data: GeoGraphType; - executeCommand: (command: Command) => void; - addCoordinatesToEdges: () => void; -} - -export interface Bundle { - [key: string]: Edge[]; -} - -export interface BundledEdge extends Edge { - from: string; - to: string; - color: number[]; - numEdges: number; - from_coordinates: number[]; - to_coordinates: number[]; -} - -export interface BundleDict { - [key: string]: Bundle; -} - -export interface Viewport { - [key: string]: any; -} - -export interface LayerProps { - data: GeoGraphType; - config: { - heatmap: Record<string, unknown>; - nodes: Record<string, unknown>; - edges: Record<string, unknown>; - }; - [key: string]: any; -} - -export interface MapProps { - graph: ViewModel | null; - mapType: number; - isAggregated: boolean; - isFanned: boolean; -} diff --git a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/utils.tsx b/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/utils.tsx deleted file mode 100644 index a88f25732ec15258dfdd5c672d43d7e7b8d740f8..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/archive/geovis/utils.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/** - * This program has been developed by a student from the bachelor Information Science at - * Utrecht University within the Research Project course. - * © Copyright Utrecht University (Department of Information and Computing Sciences) - */ - -import { GeoGraphType, Viewport, Coordinates } from './types'; -import * as d3 from 'd3'; - -export const calcMaxWidth = (viewport: Viewport): number => { - const vpBounds: number[] = viewport.getBounds(); - return Math.sqrt(Math.pow(vpBounds[0] - vpBounds[2], 2) + Math.pow(vpBounds[1] - vpBounds[3], 2)); -}; - -export const getColorFromScale = (prediction: number, colorScale: d3.ScaleLinear<number, string>): [number, number, number] => { - const color = colorScale(prediction); - const rgb: number[] = color - .replace(/[^\d,]/g, '') - .split(',') - .map(Number); - return [100, 0, 0]; -}; - -export const calcAverageCoordinates = (lat: number[], long: number[]): [number, number] => { - return [lat.reduce((p, c) => p + c, 0) / lat.length, long.reduce((p, c) => p + c, 0) / long.length]; -}; - -export const calcNodeDistance = (source: Coordinates, target: Coordinates): number => { - return Math.sqrt(Math.pow(source[0] - target[0], 2) + Math.pow(source[1] - target[1], 2)); -}; - -export const getKeys = (graph: GeoGraphType, prefix = '', paths = { nodes: new Set(), edges: new Set() }) => { - for (const key in graph) { - if (typeof graph[key] === 'object' && graph[key] !== null) { - if (Array.isArray(graph[key])) { - const newPrefix = prefix ? `${prefix}.${key}` : key; - for (let i = 0; i < graph[key].length; i++) { - getKeys(graph[key][i], newPrefix, paths); - } - } else { - const newPrefix = prefix ? `${prefix}.${key}` : key; - getKeys(graph[key], newPrefix, paths); - } - } else { - const path = prefix ? `${prefix}.${key}` : key; - if (prefix.startsWith('nodes')) { - paths.nodes.add(path); - } else if (prefix.startsWith('edges')) { - paths.edges.add(path); - } - } - } - return paths; -}; diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx deleted file mode 100644 index 7a3621f832f022de7b4295c49a81134db53160e2..0000000000000000000000000000000000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import React from 'react'; -import DeckGL from '@deck.gl/react'; -import { FlyToInterpolator, MapView, WebMercatorViewport } from '@deck.gl/core'; -import { createBaseMap } from './BaseMap'; -import FilterMenu from './FilterMenu'; -import { GraphType, Layer } from '../types'; -import { SelectionLayer } from '@deck.gl-community/editable-layers'; -import SelectedMenu from './SelectedMenu'; -import SecondaryMenu from './SecondaryMenu'; -import { VisualizationPropTypes } from '../../../common'; -import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; - -type Props = { - graph: GraphType; - layers: Layer[]; - showFilter: boolean; - setShowFilter: React.Dispatch<React.SetStateAction<boolean>>; - graphMetadata: GraphMetaData; -}; - -const INITIAL_VIEW_STATE = { - latitude: 52.1006, - longitude: 5.6464, - zoom: 6, - bearing: 0, - pitch: 0, -}; - -const FLY_SPEED = 1000; - -export function MapPanel({ - data, - graphMetadata, - layers, - setLayers, -}: VisualizationPropTypes & { - layers: any; - setLayers: any; -}) { - const [showFilter, setShowFilter] = React.useState<boolean>(false); - const [mapSize, setMapSize] = React.useState<boolean>(false); - const [viewport, setViewport] = React.useState<Record<string, any>>(INITIAL_VIEW_STATE); - const [hoverObject, setHoverObject] = React.useState<any>(null); - const [selected, setSelected] = React.useState<any[]>([]); - const [isSelecting, setIsSelecting] = React.useState<boolean>(false); - const [selectingRectangle, setSelectingRectangle] = React.useState<boolean>(false); - - const getFittedViewport = (minLat: number, maxLat: number, minLon: number, maxLon: number) => { - const viewportWebMercator = new WebMercatorViewport(viewport).fitBounds( - [ - [minLon, minLat], - [maxLon, maxLat], - ], - { padding: 20 }, - ); - const { zoom, longitude, latitude } = viewportWebMercator; - return { zoom, longitude, latitude }; - }; - - const flyToBoundingBox = (minLat: number, maxLat: number, minLon: number, maxLon: number, options = {}) => { - const fittedViewport = getFittedViewport(minLat, maxLat, minLon, maxLon); - setViewport((prevViewport) => ({ - ...prevViewport, - ...options, - ...fittedViewport, - transitionDuration: FLY_SPEED, - transitionInterpolator: new FlyToInterpolator(), - })); - }; - - const dataLayers = React.useMemo(() => { - return layers.map((layer: Layer) => { - return new layer.type({ - id: `${layer.id}`, - graph: data, - visible: layer.visible, - config: layer.config, - selected: selected, - hoverObject: hoverObject, - isSelecting: isSelecting, - flyToBoundingBox: flyToBoundingBox, - }); - }); - }, [layers, data, selected, hoverObject]); - - const handleSelect = (info: any, event: any) => { - const shiftPressed = event.srcEvent.shiftKey; - setIsSelecting(shiftPressed); - setSelected((prevSelected) => { - if (!shiftPressed) { - return info.object !== undefined ? [info.object] : []; - } else { - const selectedIndex = prevSelected.findIndex((obj) => obj === info.object); - if (selectedIndex !== -1) { - prevSelected.splice(selectedIndex, 1); - } else { - prevSelected.push(info.object); - } - return [...prevSelected]; - } - }); - }; - - const selectionLayer = - selectingRectangle && - new (SelectionLayer as any)({ - id: 'selection', - selectionType: 'rectangle', - onSelect: ({ pickingInfos }: any) => { - setSelected(pickingInfos.map((item: any) => item.object)); - setSelectingRectangle(false); - }, - layerIds: layers.map((layer) => layer.id), - getTentativeFillColor: () => [22, 37, 67, 100], - }); - - return ( - <div className={`flex-grow relative ${mapSize ? '!fixed w-screen h-screen z-10' : ''}`}> - <DeckGL - initialViewState={viewport} - controller - layers={[createBaseMap(), ...dataLayers, selectionLayer]} - onViewStateChange={({ viewState }) => setViewport(viewState)} - onClick={handleSelect} - onHover={({ object }) => setHoverObject(object !== undefined ? object : null)} - /> - {/* <SelectedMenu selected={selected} /> */} - {/* <SecondaryMenu - mapSize={mapSize} - setMapSize={setMapSize} - setViewport={setViewport} - flyToBoundingBox={flyToBoundingBox} - setSelectingRectangle={setSelectingRectangle} - selectingRectangle={selectingRectangle} - /> */} - {showFilter && <FilterMenu graphMetadata={graphMetadata} setShowFilter={setShowFilter} />} - </div> - ); -} diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/index.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/index.tsx index 463cc3e3f9b3d7abf264b6d5db78af4b4db3b5d0..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/components/index.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/index.tsx @@ -1,2 +0,0 @@ -export { LayerPanel } from './LayerPanel'; -export { MapPanel } from './MapPanel'; diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/choropleth-layer/ChoroplethLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/choropleth-layer/ChoroplethLayer.tsx similarity index 90% rename from libs/shared/lib/vis/visualizations/mapvis/layers/choropleth-layer/ChoroplethLayer.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/choropleth-layer/ChoroplethLayer.tsx index f28b7df798e819195d7365cbfd2c439b88c77e48..5e6c9c817aad72fe6595a12dccbf2aef3f765b66 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/choropleth-layer/ChoroplethLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/layers/choropleth-layer/ChoroplethLayer.tsx @@ -1,11 +1,10 @@ import React from 'react'; import { CompositeLayer } from 'deck.gl'; import { ArcLayer, GeoJsonLayer, ScatterplotLayer } from '@deck.gl/layers'; -import { getDistance } from '../../utlis'; -import * as d3 from 'd3'; import ChoroplethOptions from './ChoroplethOptions'; -import { europeData, usaData, worldData, netherlands } from '../../../../../mock-data/geo-json'; -import { Edge, Node, LayerProps, GeoJSONData } from '../../types'; +import { europeData, usaData, worldData, netherlands } from '../../../../../../mock-data/geo-json'; +import { Edge, Node, LayerProps } from '../../../mapvis.types'; +import { RGBColor, color, geoBounds, geoCentroid, geoContains, interpolateYlGnBu, scaleSequential } from 'd3'; export const circumferencesMap = { netherlands: netherlands, @@ -100,10 +99,10 @@ export class ChoroplethLayer extends CompositeLayer<LayerProps> { let flyOptions; if (props.selected.length == 0) { - bounds = d3.geoBounds(this.state.data); + bounds = geoBounds(this.state.data as any); flyOptions = { pitch: 0 }; } else if (props.selected.length == 1) { - bounds = d3.geoBounds(props.selected[0]); + bounds = geoBounds(props.selected[0]); flyOptions = { pitch: props.config.pitchOnSelect ? 50 : 0 }; } @@ -123,7 +122,7 @@ export class ChoroplethLayer extends CompositeLayer<LayerProps> { nodes.map((node: Node) => { const coordinates: [number, number] = [node.attributes.long, node.attributes.lat]; updatedGeojson.features.map((feature: any) => { - const isInside = d3.geoContains(feature, coordinates); + const isInside = geoContains(feature, coordinates); if (isInside) { feature.properties.nodes = feature.properties.nodes ?? []; feature.properties.nodes.push(node.id); @@ -139,7 +138,7 @@ export class ChoroplethLayer extends CompositeLayer<LayerProps> { feature.properties.outgoingEdges !== undefined ? feature.properties.outgoingEdges + nOutgoingEdges : nOutgoingEdges; feature.properties.townships.map((township: any) => { - const isInside = d3.geoContains(township, coordinates); + const isInside = geoContains(township, coordinates); if (isInside) { township.properties.nodes = township.properties.nodes ?? []; township.properties.nodes.push(node.id); @@ -168,11 +167,11 @@ export class ChoroplethLayer extends CompositeLayer<LayerProps> { getColor(polygon: any) { const length = polygon.properties.outgoingEdges ?? 1; - const colorScale = d3.scaleSequential(d3.interpolateYlGnBu); - const color = d3.color(colorScale(length)) as d3.RGBColor; + const colorScale = scaleSequential(interpolateYlGnBu); + const _color = color(colorScale(length)) as RGBColor; - if (color) { - return [color.r, color.g, color.b]; + if (_color) { + return [_color.r, _color.g, _color.b]; } return [100, 0, 0]; } @@ -185,7 +184,7 @@ export class ChoroplethLayer extends CompositeLayer<LayerProps> { // if (!visible) return; if (isSelecting) { - const nodes = selected.flatMap((feature: any) => feature.properties.nodes ?? []); + const nodes = (selected as any).flatMap((feature: any) => feature.properties.nodes ?? []); if (nodes.length > 0) { const filteredEdges = graph.getEdges().filter((edge: Edge) => { @@ -233,7 +232,7 @@ export class ChoroplethLayer extends CompositeLayer<LayerProps> { return nodes.some((node: string) => node === edge.from || node === edge.to); }); - const centroid = d3.geoCentroid(hoverObject); + const centroid = geoCentroid(hoverObject); if (filteredEdges.length > 0) { layers.push([ @@ -269,8 +268,8 @@ export class ChoroplethLayer extends CompositeLayer<LayerProps> { } } - data.features.forEach((feature: any) => { - const isFeatureSelected = selected.includes(feature); + (data as any).features.forEach((feature: any) => { + const isFeatureSelected = (selected as any).includes(feature); layers.push( new GeoJsonLayer( diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/choropleth-layer/ChoroplethOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/choropleth-layer/ChoroplethOptions.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/layers/choropleth-layer/ChoroplethOptions.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/choropleth-layer/ChoroplethOptions.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/edge-arc-layer/ArcLayerOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/edge-arc-layer/ArcLayerOptions.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/layers/edge-arc-layer/ArcLayerOptions.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/edge-arc-layer/ArcLayerOptions.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/edge-arc-layer/EdgeArcLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/edge-arc-layer/EdgeArcLayer.tsx similarity index 96% rename from libs/shared/lib/vis/visualizations/mapvis/layers/edge-arc-layer/EdgeArcLayer.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/edge-arc-layer/EdgeArcLayer.tsx index 9d84fc0068d70a5c6d5b2ee07a8229cbd25c869d..c57677d45430d90dbbc26030242d26a4674aec4b 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/edge-arc-layer/EdgeArcLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/layers/edge-arc-layer/EdgeArcLayer.tsx @@ -3,9 +3,9 @@ import { CompositeLayer } from 'deck.gl/typed'; import { ArcLayer, LineLayer } from '@deck.gl/layers/typed'; import ArcLayerOptions from './ArcLayerOptions'; import * as d3 from 'd3'; -import { getProperty } from '../../utlis'; +import { getProperty } from '../../../utlis'; import { BrushingExtension } from '@deck.gl/extensions/typed'; -import { Edge, LayerProps } from '../../types'; +import { Edge, LayerProps } from '../../../mapvis.types'; export const EdgeArcLayerConfig = { width: { diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/edge-layer/EdgeLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/edge-layer/EdgeLayer.tsx similarity index 98% rename from libs/shared/lib/vis/visualizations/mapvis/layers/edge-layer/EdgeLayer.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/edge-layer/EdgeLayer.tsx index f20e1bce54b9521f0acb3f9ab2004d4ce13763e4..15e5fb6ed2233f6c33ebce121ece728de5bd1fe0 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/edge-layer/EdgeLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/layers/edge-layer/EdgeLayer.tsx @@ -3,9 +3,9 @@ import { CompositeLayer } from 'deck.gl'; import { GeoJsonLayer, LineLayer, PathLayer } from '@deck.gl/layers'; import EdgeOptions from './EdgeOptions'; import * as d3 from 'd3'; -import { getDistance, getProperty } from '../../utlis'; +import { getDistance, getProperty } from '../../../utlis'; import { BrushingExtension } from '@deck.gl/extensions'; -import { Edge, LayerProps } from '../../types'; +import { Edge, LayerProps } from '../../../mapvis.types'; export const EdgeLayerConfig = { width: { diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/edge-layer/EdgeOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/edge-layer/EdgeOptions.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/layers/edge-layer/EdgeOptions.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/edge-layer/EdgeOptions.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/heatmap-layer/HeatLayer.tsx similarity index 97% rename from libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/heatmap-layer/HeatLayer.tsx index 3e9a9a6695e2cd93aeaf879d4f69804aaefd1079..dbc43b33a33bb9a435e122e5cbc85f9afecb4d6f 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/layers/heatmap-layer/HeatLayer.tsx @@ -2,8 +2,8 @@ import React from 'react'; import { CompositeLayer, HeatmapLayer } from 'deck.gl'; import HeatLayerOptions from './HeatLayerOptions'; import * as d3 from 'd3'; -import { getDistance, getProperty } from '../../utlis'; -import { Edge, LayerProps } from '../../types'; +import { getDistance, getProperty } from '../../../utlis'; +import { Edge, LayerProps } from '../../../mapvis.types'; /* Potential use cases: diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayerOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/heatmap-layer/HeatLayerOptions.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayerOptions.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/heatmap-layer/HeatLayerOptions.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/icon-layer/IconLayer.tsx similarity index 72% rename from libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconLayer.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/icon-layer/IconLayer.tsx index 7c062302200163cf7cb63eacb640750c06519a29..90b105015d914bbea2cf2cbe4f65da1d4765d76c 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/layers/icon-layer/IconLayer.tsx @@ -1,10 +1,8 @@ import React from 'react'; -import { CompositeLayer } from 'deck.gl/typed'; -import { IconLayer } from '@deck.gl/layers/typed'; -import { getProperty } from '../../utlis'; -import * as d3 from 'd3'; +import { CompositeLayer } from 'deck.gl'; +import { IconLayer } from '@deck.gl/layers'; import IconOptions from './IconOptions'; -import { LayerProps } from '../../types'; +import { LayerProps } from '../../../mapvis.types'; // TODO: Make icons based on node property @@ -26,22 +24,21 @@ export class NodeIconLayer extends CompositeLayer<LayerProps> { updateState({ props, oldProps, context, changeFlags }: { props: any; oldProps: any; context: any; changeFlags: any }) { const { graph, config } = props; - console.log(props, oldProps, context, changeFlags); } renderLayers() { - const { graph, config, visible } = this.props; + const { graph, config, visible, getNodeLocation } = this.props; return new IconLayer( this.getSubLayerProps({ hidden: visible, - data: graph.getNodes(), + data: graph.nodes, iconAtlas: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/icon-atlas.png', iconMapping: ICON_MAPPING, getIcon: (d: any) => 'marker', sizeScale: 10, - getPosition: (d: any) => [d.attributes.long, d.attributes.lat], - getSize: (d: any) => 5, + getPosition: (d: any) => getNodeLocation(d.id), + getSize: (d: any) => 3, getColor: (d: any) => [Math.sqrt(d.exits), 140, 0], }), ); diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/icon-layer/IconOptions.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconOptions.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/icon-layer/IconOptions.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/index.tsx similarity index 50% rename from libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/index.tsx index 0fe995e8852cee9379ebc9378d3f39738ee4740f..6cb501224661ecf75e31f79e7884318c0666cfb1 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/layers/index.tsx @@ -1,11 +1,13 @@ -import { EdgeLayer } from './edge-layer/EdgeLayer'; import { ChoroplethLayer } from './choropleth-layer/ChoroplethLayer'; import { HeatLayer } from './heatmap-layer/HeatLayer'; import { NodeLinkLayer } from './nodelink-layer/NodeLinkLayer'; +import { NodeLayer } from './node-layer/NodeLayer'; +import { NodeIconLayer } from './icon-layer/IconLayer'; export const layerTypes: Record<string, any> = { - NodeLink: NodeLinkLayer, - Choropleth: ChoroplethLayer, - Heatmap: HeatLayer, - Edge: EdgeLayer, + node: NodeLayer, + icon: NodeIconLayer, + // nodelink: NodeLinkLayer, + // choropleth: ChoroplethLayer, + // heatmap: HeatLayer, }; diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/node-layer/NodeLayer.tsx similarity index 61% rename from libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeLayer.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/node-layer/NodeLayer.tsx index 3eaaeb069cba172e54d0ac07be6862b82c122637..9c1a4bcd1af17c18563bc519efd747365da9f99c 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/layers/node-layer/NodeLayer.tsx @@ -1,13 +1,28 @@ import React from 'react'; import { CompositeLayer } from 'deck.gl'; import { ScatterplotLayer } from '@deck.gl/layers'; -import { getProperty } from '../../utlis'; -import * as d3 from 'd3'; +import { getProperty } from '../../../utlis'; import NodeOptions from './NodeOptions'; -import { Node, LayerProps } from '../../types'; +import { Node, LayerProps } from '../../../mapvis.types'; +import { color, scaleLinear, scaleOrdinal } from 'd3'; +import { dataColors, visualizationColors } from 'config'; -export const NodeLayerConfig = { - colisionFilter: true, +export const NodeLayerConfig: { + collisionFilter: boolean; + opacity: number; + fillColor: { + filled: boolean; + type: string; + label: string; + accessor: string; + fixed: boolean; + defaultValue: string; + value: string; + scaleRange: string[]; + condition: string; + }; +} = { + collisionFilter: true, opacity: 1, fillColor: { filled: true, @@ -15,12 +30,9 @@ export const NodeLayerConfig = { label: 'color', accessor: 'getFillColor', fixed: true, - defaultValue: [255, 0, 0], - value: [255, 0, 0], - scaleRange: [ - [255, 0, 0], - [0, 0, 255], - ], + defaultValue: visualizationColors.GPPrimary.colors[1][0], + value: visualizationColors.GPPrimary.colors[1][0], + scaleRange: visualizationColors.GPPrimary.colors[1], condition: '', }, }; @@ -44,14 +56,12 @@ export class NodeLayer extends CompositeLayer<LayerProps> { } createColorScale(colorProp: { [key: string]: any }, colorAttribute: { [key: string]: any }) { - console.log(colorAttribute); if (colorAttribute.dataType == 'boolean') { - return d3.scaleOrdinal().domain(['0', '1']).range(colorProp.scaleRange); + return scaleOrdinal().domain(['0', '1']).range(colorProp.scaleRange); } else if (colorAttribute.dataType == 'number') { - return d3.scaleLinear().domain(colorAttribute.values).range(colorProp.scaleRange); + return scaleLinear().domain(colorAttribute.values).range(colorProp.scaleRange); } else if (colorAttribute.dataType == 'string') { - return d3 - .scaleOrdinal() + return scaleOrdinal() .domain([...colorAttribute.values]) .range(colorProp.scaleRange); } else { @@ -68,16 +78,16 @@ export class NodeLayer extends CompositeLayer<LayerProps> { return colorProps.defaultValue; } const value = getProperty(node.attributes, condition); - return this.state.colorScale(value); + return (this.state as any).colorScale(value); } } renderLayers() { - const { graph, config, visible } = this.props; + const { graph, config, visible, getNodeLocation } = this.props; return new ScatterplotLayer({ hidden: visible, - data: graph.getNodes(), + data: graph.nodes, pickable: true, opacity: config.opacity, filled: config.fillColor.filled, @@ -85,8 +95,15 @@ export class NodeLayer extends CompositeLayer<LayerProps> { radiusMinPixels: 7, radiusMaxPixels: 100, lineWidthMinPixels: 1, - getPosition: (d: any) => [d.attributes.long, d.attributes.lat], - getFillColor: (d: any) => this.getColor(d, config.fillColor), + getPosition: (d: any) => getNodeLocation(d.id), + // getFillColor: (d: any) => this.getColor(d, config.fillColor), + getFillColor: (d: any) => { + const c = color(config.fillColor.value as string)?.rgb(); + if (!c) { + return [0, 0, 0]; + } + return [c.r, c.g, c.b]; + }, getRadius: (d: any) => 5, }); } diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/node-layer/NodeOptions.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeOptions.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/node-layer/NodeOptions.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/nodelink-layer/NodeLinkLayer.tsx similarity index 98% rename from libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/nodelink-layer/NodeLinkLayer.tsx index ed7e210829e55535e1a1d2d80d58a810b1179dbd..81a8c1a0b6a21fa0ae8afb0639b735b89f843f3a 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/layers/nodelink-layer/NodeLinkLayer.tsx @@ -3,8 +3,8 @@ import { CompositeLayer } from 'deck.gl'; import { IconLayer, LineLayer, TextLayer } from '@deck.gl/layers'; import NodeLinkOptions from './NodeLinkOptions'; import { createIcon } from './shapeFactory'; -import { getProperty } from '../../utlis'; -import { Edge, Node, LayerProps } from '../../types'; +import { getProperty } from '../../../utlis'; +import { Edge, Node, LayerProps } from '../../../mapvis.types'; export const NodeLinkConfig = { showLabels: false, diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/nodelink-layer/NodeLinkOptions.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/nodelink-layer/NodeLinkOptions.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/shapeFactory.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/layers/nodelink-layer/shapeFactory.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/shapeFactory.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/layers/nodelink-layer/shapeFactory.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/shared/ColorPicker.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/shared/ColorPicker.tsx similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/shared/ColorPicker.tsx rename to libs/shared/lib/vis/visualizations/mapvis/components/shared/ColorPicker.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/graphModel.tsx b/libs/shared/lib/vis/visualizations/mapvis/graphModel.tsx index 73464a385b6bdcf16e6e0becf70c973f03f593c1..5cd11593e8f8aa53c11ce6b2b1d0bdda434c9fcc 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/graphModel.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/graphModel.tsx @@ -1,4 +1,4 @@ -import { GraphType, Node, Edge, Coordinate } from './types'; +import { GraphType, Node, Edge, Coordinate } from './mapvis.types'; export default class GraphModel implements GraphType { nodeMap: { [id: string]: Node }; diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx index a89ba6db0c8b29053562878715779c4e1ddb09d9..70e01ef194e587e3c1b2b77c1d61d265b25cecaf 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx @@ -37,12 +37,14 @@ const Component: Meta<typeof MapComponent.component> = { export const DutchVehicleTheft = { args: { data: mockMobilityQueryResult, + configuration: MapComponent.configuration, }, }; export const AmericanFlights = { args: { data: bigMockQueryResults, + configuration: MapComponent.configuration, }, }; diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx index cd843f1ce46c61ddbd24d64b4a74bdf071f653cc..33916998347af77bb09a4adc621905e872a3b826 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx @@ -1,61 +1,231 @@ -import React, { useEffect } from 'react'; -import { MapPanel, LayerPanel } from './components'; -import GraphModel from './graphModel'; -import { GraphType, Layer } from './types'; +import React, { useEffect, useMemo } from 'react'; +import DeckGL from '@deck.gl/react'; +import { FlyToInterpolator, MapView, WebMercatorViewport } from '@deck.gl/core'; +import { SelectionLayer } from '@deck.gl-community/editable-layers'; +import { Coordinate, Layer } from './mapvis.types'; import { VISComponentType, VisualizationPropTypes } from '../../common'; -import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; -import { makeLayer } from './utlis'; +import { SettingsContainer } from '../../components/config'; +import { layerTypes } from './components/layers'; +import { createBaseMap } from './components/BaseMap'; +import { Input, useML } from '../../..'; +import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; -export type MapProps = {}; +export type MapProps = { + layer: undefined | string; + node: undefined | string; + lat: undefined | string; + lon: undefined | string; +}; const configuration: MapProps = { - layerName: 'nodeLayer', + layer: 'node', + node: undefined, + lat: 'gp_latitude', + lon: 'gp_longitude', +}; + +const INITIAL_VIEW_STATE = { + latitude: 52.1006, + longitude: 5.6464, + zoom: 6, + bearing: 0, + pitch: 0, }; -export default function MapVis(props: VisualizationPropTypes) { - const [layers, setLayers] = React.useState<Layer[]>([]); - const [showFilter, setShowFilter] = React.useState<boolean>(false); +const FLY_SPEED = 1000; + +export const MapVis = ({ data, configuration, updateSettings, graphMetadata }: VisualizationPropTypes) => { + const [layer, setLayer] = React.useState<Layer | undefined>(undefined); + const [viewport, setViewport] = React.useState<Record<string, any>>(INITIAL_VIEW_STATE); + const [hoverObject, setHoverObject] = React.useState<Node | null>(null); + const [selected, setSelected] = React.useState<any[]>([]); + const [isSelecting, setIsSelecting] = React.useState<boolean>(false); + const [selectingRectangle, setSelectingRectangle] = React.useState<boolean>(false); - const createLayer = (type: string) => { - const newLayer = makeLayer(type); - setLayers([...layers, newLayer]); + const getFittedViewport = (minLat: number, maxLat: number, minLon: number, maxLon: number) => { + const viewportWebMercator = new WebMercatorViewport(viewport).fitBounds( + [ + [minLon, minLat], + [maxLon, maxLat], + ], + { padding: 20 }, + ); + const { zoom, longitude, latitude } = viewportWebMercator; + return { zoom, longitude, latitude }; + }; + + const flyToBoundingBox = (minLat: number, maxLat: number, minLon: number, maxLon: number, options = {}) => { + const fittedViewport = getFittedViewport(minLat, maxLat, minLon, maxLon); + setViewport((prevViewport) => ({ + ...prevViewport, + ...options, + ...fittedViewport, + transitionDuration: FLY_SPEED, + transitionInterpolator: new FlyToInterpolator(), + })); }; useEffect(() => { - createLayer(props.configuration.layerName); - }, []); - - // let graph = React.useMemo(() => { - // try { - // const graphModel = new GraphModel(); - // graphModel.consumeMessageFromBackend(data); - // return graphModel; - // } catch (e) { - // console.error(e); - // return null; - // } - // }, [data]); - - // if (!graph) return <div>No data</div>; + if (configuration.layer) { + const layerType = layerTypes[configuration.layer] as any; + + setLayer({ + id: Date.now(), + name: 'New layer', + type: layerType, + config: { + ...layerType.layerOptions, + }, + visible: true, + }); + } + }, [configuration.layer]); + + useEffect(() => { + if (!graphMetadata.nodes.labels.includes(configuration.node)) { + updateSettings({ node: undefined }); + } + }, [graphMetadata.nodes.types, data, configuration]); + + const dataLayer = useMemo(() => { + if (!layer || !configuration.node || !configuration.lat || !configuration.lon) return null; + + const coordinateLookup: { [id: string]: Coordinate } = data.nodes.reduce( + (acc, node) => { + // const latitude = getProperty(node, configuration.lat); + // const longitude = getProperty(node, configuration.lon); + const latitude = node?.attributes?.[configuration.lat] as string | undefined; + const longitude = node?.attributes?.[configuration.lon] as string | undefined; + + if (!!latitude && !!longitude) { + acc[node.id] = [parseFloat(longitude), parseFloat(latitude)]; + } + + return acc; + }, + {} as { [id: string]: Coordinate }, + ); + + return new layer.type({ + id: `${layer.id}`, + graph: data, + visible: layer.visible, + config: layer.config, + selected: selected, + hoverObject: hoverObject, + isSelecting: isSelecting, + getNodeLocation: (id: string) => coordinateLookup[id], + flyToBoundingBox: flyToBoundingBox, + }); + }, [layer, data, selected, hoverObject, isSelecting, configuration.lat, configuration.lon, configuration.node]); + + const selectionLayer = useMemo( + () => + selectingRectangle && + new (SelectionLayer as any)({ + id: 'selection', + selectionType: 'rectangle', + onSelect: ({ pickingInfos }: any) => { + setSelected(pickingInfos.map((item: any) => item.object)); + setSelectingRectangle(false); + }, + layerIds: [layer?.id ? layer.id : ''], + getTentativeFillColor: () => [22, 37, 67, 100], + }), + [selectingRectangle, layer], + ); + + const handleSelect = (info: any, event: any) => { + const shiftPressed = event.srcEvent.shiftKey; + setIsSelecting(shiftPressed); + setSelected((prevSelected) => { + if (!shiftPressed) { + return info.object !== undefined ? [info.object] : []; + } else { + const selectedIndex = prevSelected.findIndex((obj) => obj === info.object); + if (selectedIndex !== -1) { + prevSelected.splice(selectedIndex, 1); + } else { + prevSelected.push(info.object); + } + return [...prevSelected]; + } + }); + }; return ( - <div className="flex flex-row justify-between overflow-hidden h-full w-full font-sans"> - <MapPanel {...props} layers={layers} setLayers={setLayers} /> - {/* <LayerPanel layers={layers} setLayers={setLayers} graphInfo={graph.getGraphInfo()} setShowFilter={setShowFilter} /> */} + <div className="w-full h-full flex-grow relative"> + <DeckGL + layers={[createBaseMap(), dataLayer, selectionLayer]} + controller={true} + initialViewState={viewport} + onViewStateChange={({ viewState }) => setViewport(viewState)} + onClick={handleSelect} + onHover={({ object }) => { + setHoverObject(object !== undefined ? object : null); + }} + /> </div> ); -} +}; const MapSettings = ({ configuration, - graph, + graphMetadata, updateSettings, }: { configuration: MapProps; - graph: GraphMetaData; + graphMetadata: GraphMetadata; updateSettings: (val: any) => void; }) => { - return <div>To be implemented</div>; + // const spatialAttributes = useMemo(() => { + // if (!configuration.node) return []; + // return Object.entries(graphMetadata.nodes.types[configuration.node].attributes) + // .filter((kv) => kv[1].dimension === 'spatial') + // .map((kv) => kv[0]); + // }, [configuration.node]); + const spatialAttributes = useMemo(() => { + if (!configuration.node) return []; + return Object.entries(graphMetadata.nodes.types[configuration.node].attributes).map((kv) => kv[0]); + }, [configuration.node]); + + return ( + <SettingsContainer> + <span className="text-xs font-semibold">Data layer</span> + <Input + type="dropdown" + value={configuration.layer} + options={Object.keys(layerTypes)} + onChange={(val) => updateSettings({ layer: val })} + /> + + <span className="text-xs font-semibold">Node Label</span> + <Input + type="dropdown" + value={configuration.node} + options={[...Object.keys(graphMetadata.nodes.types)]} + disabled={Object.keys(graphMetadata.nodes.types).length < 1} + onChange={(val) => updateSettings({ node: val })} + /> + <span className="text-xs font-semibold">Location accessor (lat)</span> + <Input + type="dropdown" + value={configuration.lat} + options={[...spatialAttributes]} + disabled={!configuration.node || spatialAttributes.length < 1} + onChange={(val) => updateSettings({ lat: val })} + /> + + <span className="text-xs font-semibold">Location accessor (lon)</span> + <Input + type="dropdown" + value={configuration.lon} + options={[...spatialAttributes]} + disabled={!configuration.node || spatialAttributes.length < 1} + onChange={(val) => updateSettings({ lon: val })} + /> + </SettingsContainer> + ); }; export const MapComponent: VISComponentType = { @@ -64,3 +234,5 @@ export const MapComponent: VISComponentType = { settings: MapSettings, configuration: configuration, }; + +export default MapComponent; diff --git a/libs/shared/lib/vis/visualizations/mapvis/types.ts b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts similarity index 100% rename from libs/shared/lib/vis/visualizations/mapvis/types.ts rename to libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts diff --git a/libs/shared/lib/vis/visualizations/mapvis/utlis.tsx b/libs/shared/lib/vis/visualizations/mapvis/utlis.tsx index 8f5e38de3aae97ce22ddfd6984fef09b03209167..f6fa9b0659219d4fc6b35a412552b6d31bd4d008 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/utlis.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/utlis.tsx @@ -1,6 +1,4 @@ -import { Coordinate, Layer } from './types'; -import { layerTypes } from './layers'; -import { Layer as DeckLayer } from '@deck.gl/core/typed'; +import { Coordinate } from './mapvis.types'; export const getProperty = (obj: any, accessorString: string): any => { const accessors = accessorString.split('.'); @@ -14,24 +12,6 @@ export const getProperty = (obj: any, accessorString: string): any => { return value; }; -export const makeLayer = (type: string): Layer => { - const layerType = layerTypes[type] as any; - - if (!layerType) { - throw new Error(`Invalid layer type: ${type}`); - } - - return { - id: Date.now(), - name: 'New layer', - type: layerType, - config: { - ...layerType.layerOptions, - }, - visible: true, - }; -}; - export const getLocationInfo = async (search: string) => { try { const query: string = encodeURIComponent(search); diff --git a/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPixi.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPixi.tsx index 04f26df0cd3ed58e6a551718bd8e2cbdf7a57ea9..7d21ffe03ebf21c694e640497b0cc4f34446c66f 100644 --- a/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPixi.tsx +++ b/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPixi.tsx @@ -1,6 +1,5 @@ import { Edge, GraphQueryResult, Node, useML, useSearchResultData } from '@graphpolaris/shared/lib/data-access'; import { dataColors, visualizationColors } from 'config'; -import * as d3 from 'd3'; import { Viewport } from 'pixi-viewport'; import { Application, @@ -25,7 +24,7 @@ import Color from 'color'; import { createColumn } from './ColumnGraphicsComponent'; import { ReorderingManager } from './ReorderingManager'; import { VisualizationConfiguration } from '../../../common'; -import { vi } from 'vitest'; +import { range, scaleLinear, scaleOrdinal, schemeCategory10 } from 'd3'; type Props = { // onClick: (node: NodeType, pos: IPointData) => void; @@ -335,8 +334,8 @@ export const MatrixPixi = (props: Props) => { return; } - columnOrder = d3.range(0, cols.length).map((d) => cols[d].id); - rowOrder = d3.range(0, rows.length).map((d) => rows[d].id); + columnOrder = range(0, cols.length).map((d) => cols[d].id); + rowOrder = range(0, rows.length).map((d) => rows[d].id); const reorderingManager = new ReorderingManager(props.graph); const newOrdering = reorderingManager.reorderMatrix('count', columnOrder, rowOrder); columnOrder = newOrdering.columnOrder; @@ -352,7 +351,7 @@ export const MatrixPixi = (props: Props) => { setupColumnInteractivity(cols); setupRowLegend(rows, rowOrder); - setupRowInteractivity(cols, rows, rowOrder, d3.scaleOrdinal(d3.schemeCategory10)); + setupRowInteractivity(cols, rows, rowOrder, scaleOrdinal(schemeCategory10)); console.debug('setup matrixvis with graph:', props.graph); @@ -372,8 +371,8 @@ export const MatrixPixi = (props: Props) => { const colorNeutral = Color(colorNeutralString); // make adjacency - // const adjacenyScale = d3.scaleLinear([dataColors.neutral['5'], tailwindColors.entity.DEFAULT]); - const adjacenyScale = d3.scaleLinear([colorNeutral, visualizationColors.GPSelected.colors[1][0]]); + // const adjacenyScale = scaleLinear([dataColors.neutral['5'], tailwindColors.entity.DEFAULT]); + const adjacenyScale = scaleLinear([colorNeutral, visualizationColors.GPSelected.colors[1][0]]); visMapping.push({ attribute: 'adjacency', encoding: configuration.marks, @@ -402,7 +401,7 @@ export const MatrixPixi = (props: Props) => { // return edge.label; // }); // const tailwindColorsArray = Object.values(tailwindColors.entity).slice(0, labels.length); - // const scale = d3.scaleOrdinal(tailwindColorsArray.reverse()); + // const scale = scaleOrdinal(tailwindColorsArray.reverse()); // scale.domain(Array.from(labels)); // console.log('labels', labels, props.graph.edges); // visMapping.push({ attribute: 'label', encoding: 'rect', colorScale: scale }); @@ -412,11 +411,11 @@ export const MatrixPixi = (props: Props) => { // return edge?.attributes?.Distance as number; // }); - // const extent = d3.extent(values); + // const extent = extent(values); // if (extent[0] === undefined || extent[1] === undefined) throw new Error('Extent is undefined'); // console.log('labels', values, extent, props.graph.edges); - // const scale = d3.scaleLinear(['red', 'blue']); + // const scale = scaleLinear(['red', 'blue']); // scale.domain(extent as [number, number]); // visMapping.push({ attribute: 'attributes.Distance', encoding: 'rect', colorScale: scale }); diff --git a/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx b/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx index b5ee7fe95c27ade3dfc02ce1656f101627379c7d..c225b2994efdef363f4574076dcbad968f9986cd 100644 --- a/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx +++ b/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx @@ -5,9 +5,8 @@ import { LinkType, NodeType } from './types'; import { MatrixPixi } from './components/MatrixPixi'; import { VisualizationPropTypes, VISComponentType } from '../../common'; import { Input } from '@graphpolaris/shared/lib/components/inputs'; -import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; +import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config'; -import { dataColors, tailwindColors } from 'config'; export interface MatrixVisProps { marks: string; @@ -46,7 +45,7 @@ const MatrixSettings = ({ updateSettings, }: { configuration: MatrixVisProps; - graph: GraphMetaData; + graph: GraphMetadata; updateSettings: (val: any) => void; }) => { return ( diff --git a/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx index 05d7993c858e18ef21e09b2916b6ef98c50b2aa4..dc2d5a53e16cb04143b42d411daf807750a70478 100644 --- a/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx +++ b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx @@ -1,16 +1,14 @@ import { GraphType, LinkType, NodeType } from '../types'; import { dataColors, visualizationColors } from 'config'; import { ReactEventHandler, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; -import { Application, Circle, Color, Container, FederatedPointerEvent, Graphics, IPointData, Point } from 'pixi.js'; -import * as force from './NLForce'; -import { Viewport } from 'pixi-viewport'; +import { Application, Circle, Color, Container, FederatedPointerEvent, Graphics, IPointData } from "pixi.js"; import { useAppDispatch, useML, useSearchResultData } from '../../../../data-access'; import { NLPopup } from './NLPopup'; import { hslStringToHex, nodeColor } from './utils'; import { CytoscapeLayout, GraphologyLayout, LayoutFactory, Layouts } from '../../../../graph-layout'; import { MultiGraph } from 'graphology'; import { VisualizationConfiguration } from '../../../common'; -import { data } from 'happy-dom/lib/PropertySymbol'; +import { Viewport } from 'pixi-viewport'; type Props = { onClick: (node: NodeType, pos: IPointData) => void; @@ -105,7 +103,6 @@ export const NLPixi = (props: Props) => { onDragMove(movementX: number, movementY: number) { if (dragging.current) { onlyClicked.current = false; - // if (popups.length > 0) setPopups([]); if (quickPopup) setQuickPopup(undefined); const idx = popups.findIndex((p) => p.node.id === dragging.current?.node.id); if (idx >= 0) { @@ -164,11 +161,6 @@ export const NLPixi = (props: Props) => { }, })); - // useEffect(() => { - // app.renderer.resize(props.windowSize.width, props.windowSize.height); - // app.render(); - // }, [props.windowSize]); - function resize() { const width = ref?.current?.clientWidth || 1000; const height = ref?.current?.clientHeight || 1000; diff --git a/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx index 2af16aca3afde3f1bd4dd7f5e51a478f7aa3a240..ab57020dce7132ee215318f510adc3d790d60ccd 100644 --- a/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx +++ b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx @@ -6,7 +6,7 @@ import { useImmer } from 'use-immer'; import { ML, setShortestPathSource, setShortestPathTarget } from '../../../data-access/store/mlSlice'; import { Layouts } from '../../../graph-layout/types'; import { Input } from '@graphpolaris/shared/lib/components/inputs'; -import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; +import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; import { SettingsContainer, SettingsHeader } from '@graphpolaris/shared/lib/vis/components/config'; import { VISComponentType, VisualizationPropTypes } from '../../common'; @@ -103,11 +103,11 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch, configuration }: Vi const NodelinkSettings = ({ configuration, - graph, + graphMetadata, updateSettings, }: { configuration: NodelinkVisProps; - graph: GraphMetaData; + graphMetadata: GraphMetadata; updateSettings: (val: any) => void; }) => { return ( diff --git a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx index 0159bbe368ba880de96e85f2909c65fff2e96829..44ff4e32c31b32dee66839cb4d526e2c97efb4bf 100644 --- a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx +++ b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx @@ -21,13 +21,13 @@ import { HyperEdgeRange } from './components/HyperEdgesRange'; import { ToPaohvisDataParserUseCase } from './utils/ToPaohvisDataParserUsecase'; import { MakePaohvisMenu } from './components/MakePaohvisMenu'; import { RowLabelColumn } from './components/RowLabelColumn'; -import { visualizationColors } from '../../../../../config/src/colors.js'; -import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; +import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config'; import { Input } from '@graphpolaris/shared/lib/components/inputs'; import { VisualizationPropTypes, VISComponentType } from '../../common'; import { isNodeLinkResult } from './utils/ResultNodeLinkParserUseCase'; import { calculateAttributesAndRelations, calculateAttributesFromRelation, calcTextWidthAndStringText } from './utils/utils'; +import { visualizationColors } from 'config'; type PaohvisViewModelState = { //rowHeight: number; @@ -768,7 +768,7 @@ const PaohSettings = ({ updateSettings, }: { configuration: PaohVisProps; - graph: GraphMetaData; + graph: GraphMetadata; updateSettings: (val: any) => void; }) => { return ( diff --git a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx index fa8ec23553a90a924004f48485e4f065e9c742e3..7cb0ed968add55235f6949a80c91889d1d4d5f56 100644 --- a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx +++ b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; import ReactJSONView from 'react-json-view'; import { VisualizationPropTypes, VISComponentType } from '../../common'; -import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; +import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config'; import { Input } from '@graphpolaris/shared/lib/components/inputs'; @@ -43,7 +43,7 @@ const RawJSONSettings = ({ updateSettings, }: { configuration: RawJSONVisProps; - graph: GraphMetaData; + graph: GraphMetadata; updateSettings: (val: any) => void; }) => { return ( diff --git a/libs/shared/lib/vis/visualizations/semanticsubstratesvis/semanticsubstratesvis.tsx b/libs/shared/lib/vis/visualizations/semanticsubstratesvis/semanticsubstratesvis.tsx index 28069edc4e643589850c2e16165477affde653ca..fad82258d6918574578d0c53a820945305bafe5f 100644 --- a/libs/shared/lib/vis/visualizations/semanticsubstratesvis/semanticsubstratesvis.tsx +++ b/libs/shared/lib/vis/visualizations/semanticsubstratesvis/semanticsubstratesvis.tsx @@ -1,6 +1,6 @@ import React, { useRef, useState, useMemo, useEffect } from 'react'; import Scatterplot, { KeyedScatterplotProps } from './components/Scatterplot'; -import { GraphMetaData } from '@graphpolaris/shared/lib/data-access/statistics'; +import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config'; import { visualizationColors } from 'config/src/colors'; @@ -429,7 +429,7 @@ const SemSubstrSettings = ({ updateSettings, }: { configuration: SemSubstrProps; - graph: GraphMetaData; + graph: GraphMetadata; updateSettings: (val: any) => void; }) => { return ( diff --git a/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx index 929cd5b63d60c117d3ad5befc341b7f26e6310f3..3aa5192f3b870002ece95ad58fb7ddf04a479294 100644 --- a/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx +++ b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx @@ -3,7 +3,7 @@ import { Table, AugmentedNodeAttributes } from './components/Table'; import { SchemaAttribute } from '../../../schema'; import { VisualizationPropTypes, VISComponentType } from '../../common'; import { Input } from '@graphpolaris/shared/lib/components/inputs'; -import { GraphMetaData as GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; +import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config'; export type TableProps = { diff --git a/libs/shared/package.json b/libs/shared/package.json index d5b0207de4acaaa69e5e9dec037e6f7ad7d40199..4f521007ad43c91228a781b170bcfd1a0d621567 100644 --- a/libs/shared/package.json +++ b/libs/shared/package.json @@ -51,8 +51,8 @@ "lodash-es": "^4.17.21", "moment": "^2.30.1", "pixi-actions": "^1.1.10", - "pixi-viewport": "^5.0.2", - "pixi.js": "^7.4.0", + "pixi-viewport": "5.0.2", + "pixi.js": "^7.4.2", "react-color": "^2.19.3", "react-cookie": "^7.1.0", "react-draggable": "^4.4.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d470d8ec85b0e9713cf482dc54d538990f16b9fb..5cb24055668d15769296242fa465f22cfc5b0fb4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -234,7 +234,7 @@ importers: version: 1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@reactflow/node-resizer': specifier: ^2.2.9 - version: 2.2.12(@types/react@18.3.1)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 2.2.13(@types/react@18.3.1)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@reduxjs/toolkit': specifier: ^2.2.1 version: 2.2.3(react-redux@9.1.1(@types/react@18.3.1)(react@18.2.0)(redux@5.0.1))(react@18.2.0) @@ -314,10 +314,10 @@ importers: specifier: ^1.1.10 version: 1.1.11(pixi.js@7.4.2) pixi-viewport: - specifier: ^5.0.2 - version: 5.1.0(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/interaction@6.5.10(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/math@7.4.2)(@pixi/ticker@7.4.2)(@pixi/utils@7.4.2))(@pixi/math@7.4.2)(@pixi/ticker@7.4.2) + specifier: 5.0.2 + version: 5.0.2 pixi.js: - specifier: ^7.4.0 + specifier: ^7.4.2 version: 7.4.2 react-color: specifier: ^2.19.3 @@ -333,7 +333,7 @@ importers: version: 1.4.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-json-view: specifier: ^1.21.3 - version: 1.21.3(@types/react@18.3.1)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 1.21.3(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-router-dom: specifier: ^6.22.3 version: 6.23.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -584,7 +584,7 @@ importers: devDependencies: '@storybook/addon-essentials': specifier: ^8.0.6 - version: 8.0.9(@types/react@18.3.1)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 8.0.9(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/addon-interactions': specifier: ^8.0.6 version: 8.0.9(vitest@1.5.3(@types/node@20.11.27)(happy-dom@13.10.1)(jsdom@24.0.0)(sass@1.75.0)(terser@5.31.0)) @@ -593,16 +593,16 @@ importers: version: 8.0.9(react@18.2.0) '@storybook/blocks': specifier: ^8.0.6 - version: 8.0.9(@types/react@18.3.1)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 8.0.9(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/preset-scss': specifier: ^1.0.3 version: 1.0.3(css-loader@7.1.1(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.20.2)))(sass-loader@14.2.1(sass@1.75.0)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.20.2)))(style-loader@4.0.0(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.20.2))) '@storybook/react': specifier: ^8.0.6 - version: 8.0.9(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.4.5) + version: 8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.4.5) '@storybook/react-vite': specifier: ^8.0.6 - version: 8.0.9(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.10(@types/node@20.11.27)(sass@1.75.0)(terser@5.31.0)) + version: 8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.10(@types/node@20.11.27)(sass@1.75.0)(terser@5.31.0)) '@types/node': specifier: 20.11.27 version: 20.11.27 @@ -647,7 +647,7 @@ importers: version: 14.2.1(sass@1.75.0)(webpack@5.91.0(@swc/core@1.4.17(@swc/helpers@0.5.5))(esbuild@0.20.2)) storybook: specifier: ^8.0.6 - version: 8.0.9(@babel/preset-env@7.24.5(@babel/core@7.24.5))(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 8.0.9(@babel/preset-env@7.24.5(@babel/core@7.24.5))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) tailwindcss: specifier: ^3.4.1 version: 3.4.3(ts-node@10.9.2(@swc/core@1.4.17(@swc/helpers@0.5.5))(@types/node@20.11.27)(typescript@5.4.5)) @@ -2369,15 +2369,6 @@ packages: '@pixi/display': 7.4.2 '@pixi/sprite': 7.4.2 - '@pixi/interaction@6.5.10': - resolution: {integrity: sha512-v809pJmXA2B9dV/vdrDMUqJT+fBB/ARZli2YRmI2dPbEbkaYr8FNmxCAJnwT8o+ymTx044Ie820hn9tVrtMtfA==} - peerDependencies: - '@pixi/core': 6.5.10 - '@pixi/display': 6.5.10 - '@pixi/math': 6.5.10 - '@pixi/ticker': 6.5.10 - '@pixi/utils': 6.5.10 - '@pixi/math@7.4.2': resolution: {integrity: sha512-7jHmCQoYk6e0rfSKjdNFOPl0wCcdgoraxgteXJTTHv3r0bMNx2pHD9FJ0VvocEUG7XHfj55O3+u7yItOAx0JaQ==} @@ -2806,8 +2797,8 @@ packages: react: '>=17' react-dom: '>=17' - '@reactflow/core@11.11.2': - resolution: {integrity: sha512-+GfgyskweL1PsgRSguUwfrT2eDotlFgaKfDLm7x0brdzzPJY2qbCzVetaxedaiJmIli3817iYbILvE9qLKwbRA==} + '@reactflow/core@11.11.3': + resolution: {integrity: sha512-+adHdUa7fJSEM93fWfjQwyWXeI92a1eLKwWbIstoCakHpL8UjzwhEh6sn+mN2h/59MlVI7Ehr1iGTt3MsfcIFA==} peerDependencies: react: '>=17' react-dom: '>=17' @@ -2818,8 +2809,8 @@ packages: react: '>=17' react-dom: '>=17' - '@reactflow/node-resizer@2.2.12': - resolution: {integrity: sha512-6LHJGuI1zHyRrZHw5gGlVLIWnvVxid9WIqw8FMFSg+oF2DuS3pAPwSoZwypy7W22/gDNl9eD1Dcl/OtFtDFQ+w==} + '@reactflow/node-resizer@2.2.13': + resolution: {integrity: sha512-X7ceQ2s3jFLgbkg03n2RYr4hm3jTVrzkW2W/8ANv/SZfuVmF8XJxlERuD8Eka5voKqLda0ywIZGAbw9GoHLfUQ==} peerDependencies: react: '>=17' react-dom: '>=17' @@ -5123,8 +5114,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.4.751: - resolution: {integrity: sha512-2DEPi++qa89SMGRhufWTiLmzqyuGmNF3SK4+PQetW1JKiZdEpF4XQonJXJCzyuYSA6mauiMhbyVhqYAP45Hvfw==} + electron-to-chromium@1.4.752: + resolution: {integrity: sha512-P3QJreYI/AUTcfBVrC4zy9KvnZWekViThgQMX/VpJ+IsOBbcX5JFpORM4qWapwWQ+agb2nYAOyn/4PMXOk0m2Q==} elkjs@0.8.2: resolution: {integrity: sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==} @@ -5143,9 +5134,6 @@ packages: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} - encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} @@ -7026,13 +7014,8 @@ packages: peerDependencies: pixi.js: '>7.0.0' - pixi-viewport@5.1.0: - resolution: {integrity: sha512-CNvw0H+Z+OfVCIAUkQ5TmwLEe+7W1dcIS6Wjt+AwQXWEjZPL3QpH2xFE/GAaqZ0qRkDxDV07dbRetZULFOvoew==} - peerDependencies: - '@pixi/display': ^6.5.8 - '@pixi/interaction': ^6.5.8 - '@pixi/math': ^6.5.8 - '@pixi/ticker': ^6.5.8 + pixi-viewport@5.0.2: + resolution: {integrity: sha512-U77KnCTl81xEgxEQRFEuI7MYVySWwCVkA41EnM8KiOYwgVOwdBUa7318O+u61IOnTwnoYLzaihy/kpoONKU13Q==} pixi.js@7.4.2: resolution: {integrity: sha512-TifqgHGNofO7UCEbdZJOpUu7dUnpu4YZ0o76kfCqxDa4RS8ITc9zjECCbtalmuNXkVhSEZmBKQvE7qhHMqw/xg==} @@ -10830,14 +10813,6 @@ snapshots: '@pixi/display': 7.4.2(@pixi/core@7.4.2) '@pixi/sprite': 7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)) - '@pixi/interaction@6.5.10(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/math@7.4.2)(@pixi/ticker@7.4.2)(@pixi/utils@7.4.2)': - dependencies: - '@pixi/core': 7.4.2 - '@pixi/display': 7.4.2(@pixi/core@7.4.2) - '@pixi/math': 7.4.2 - '@pixi/ticker': 7.4.2 - '@pixi/utils': 7.4.2 - '@pixi/math@7.4.2': {} '@pixi/mesh-extras@7.4.2(@pixi/core@7.4.2)(@pixi/mesh@7.4.2(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2)))': @@ -11288,7 +11263,7 @@ snapshots: - '@types/react' - immer - '@reactflow/core@11.11.2(@types/react@18.3.1)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@reactflow/core@11.11.3(@types/react@18.3.1)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@types/d3': 7.4.3 '@types/d3-drag': 3.0.7 @@ -11320,9 +11295,9 @@ snapshots: - '@types/react' - immer - '@reactflow/node-resizer@2.2.12(@types/react@18.3.1)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@reactflow/node-resizer@2.2.13(@types/react@18.3.1)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@reactflow/core': 11.11.2(@types/react@18.3.1)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@reactflow/core': 11.11.3(@types/react@18.3.1)(immer@10.1.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) classcat: 5.0.5 d3-drag: 3.0.0 d3-selection: 3.0.0 @@ -11480,9 +11455,9 @@ snapshots: memoizerific: 1.11.3 ts-dedent: 2.2.0 - '@storybook/addon-controls@8.0.9(@types/react@18.3.1)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@storybook/addon-controls@8.0.9(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@storybook/blocks': 8.0.9(@types/react@18.3.1)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@storybook/blocks': 8.0.9(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) lodash: 4.17.21 ts-dedent: 2.2.0 transitivePeerDependencies: @@ -11492,11 +11467,11 @@ snapshots: - react-dom - supports-color - '@storybook/addon-docs@8.0.9(encoding@0.1.13)': + '@storybook/addon-docs@8.0.9': dependencies: '@babel/core': 7.24.5 '@mdx-js/react': 3.0.1(@types/react@18.3.1)(react@18.2.0) - '@storybook/blocks': 8.0.9(@types/react@18.3.1)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@storybook/blocks': 8.0.9(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/client-logger': 8.0.9 '@storybook/components': 8.0.9(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/csf-plugin': 8.0.9 @@ -11518,18 +11493,18 @@ snapshots: - encoding - supports-color - '@storybook/addon-essentials@8.0.9(@types/react@18.3.1)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@storybook/addon-essentials@8.0.9(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@storybook/addon-actions': 8.0.9 '@storybook/addon-backgrounds': 8.0.9 - '@storybook/addon-controls': 8.0.9(@types/react@18.3.1)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@storybook/addon-docs': 8.0.9(encoding@0.1.13) + '@storybook/addon-controls': 8.0.9(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@storybook/addon-docs': 8.0.9 '@storybook/addon-highlight': 8.0.9 '@storybook/addon-measure': 8.0.9 '@storybook/addon-outline': 8.0.9 '@storybook/addon-toolbars': 8.0.9 '@storybook/addon-viewport': 8.0.9 - '@storybook/core-common': 8.0.9(encoding@0.1.13) + '@storybook/core-common': 8.0.9 '@storybook/manager-api': 8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/node-logger': 8.0.9 '@storybook/preview-api': 8.0.9 @@ -11584,14 +11559,14 @@ snapshots: dependencies: memoizerific: 1.11.3 - '@storybook/blocks@8.0.9(@types/react@18.3.1)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@storybook/blocks@8.0.9(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@storybook/channels': 8.0.9 '@storybook/client-logger': 8.0.9 '@storybook/components': 8.0.9(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/core-events': 8.0.9 '@storybook/csf': 0.1.6 - '@storybook/docs-tools': 8.0.9(encoding@0.1.13) + '@storybook/docs-tools': 8.0.9 '@storybook/global': 5.0.0 '@storybook/icons': 1.2.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/manager-api': 8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -11618,10 +11593,10 @@ snapshots: - encoding - supports-color - '@storybook/builder-manager@8.0.9(encoding@0.1.13)': + '@storybook/builder-manager@8.0.9': dependencies: '@fal-works/esbuild-plugin-global-externals': 2.1.2 - '@storybook/core-common': 8.0.9(encoding@0.1.13) + '@storybook/core-common': 8.0.9 '@storybook/manager': 8.0.9 '@storybook/node-logger': 8.0.9 '@types/ejs': 3.1.5 @@ -11638,11 +11613,11 @@ snapshots: - encoding - supports-color - '@storybook/builder-vite@8.0.9(encoding@0.1.13)(typescript@5.4.5)(vite@5.2.10(@types/node@20.11.27)(sass@1.75.0)(terser@5.31.0))': + '@storybook/builder-vite@8.0.9(typescript@5.4.5)(vite@5.2.10(@types/node@20.11.27)(sass@1.75.0)(terser@5.31.0))': dependencies: '@storybook/channels': 8.0.9 '@storybook/client-logger': 8.0.9 - '@storybook/core-common': 8.0.9(encoding@0.1.13) + '@storybook/core-common': 8.0.9 '@storybook/core-events': 8.0.9 '@storybook/csf-plugin': 8.0.9 '@storybook/node-logger': 8.0.9 @@ -11672,18 +11647,18 @@ snapshots: telejson: 7.2.0 tiny-invariant: 1.3.3 - '@storybook/cli@8.0.9(@babel/preset-env@7.24.5(@babel/core@7.24.5))(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@storybook/cli@8.0.9(@babel/preset-env@7.24.5(@babel/core@7.24.5))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/core': 7.24.5 '@babel/types': 7.24.5 '@ndelangen/get-tarball': 3.0.9 '@storybook/codemod': 8.0.9 - '@storybook/core-common': 8.0.9(encoding@0.1.13) + '@storybook/core-common': 8.0.9 '@storybook/core-events': 8.0.9 - '@storybook/core-server': 8.0.9(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@storybook/core-server': 8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/csf-tools': 8.0.9 '@storybook/node-logger': 8.0.9 - '@storybook/telemetry': 8.0.9(encoding@0.1.13) + '@storybook/telemetry': 8.0.9 '@storybook/types': 8.0.9 '@types/semver': 7.5.8 '@yarnpkg/fslib': 2.10.3 @@ -11759,7 +11734,7 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@storybook/core-common@8.0.9(encoding@0.1.13)': + '@storybook/core-common@8.0.9': dependencies: '@storybook/core-events': 8.0.9 '@storybook/csf-tools': 8.0.9 @@ -11779,7 +11754,7 @@ snapshots: glob: 10.3.12 handlebars: 4.7.8 lazy-universal-dotenv: 4.0.0 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 picomatch: 2.3.1 pkg-dir: 5.0.0 pretty-hrtime: 1.0.3 @@ -11797,14 +11772,14 @@ snapshots: dependencies: ts-dedent: 2.2.0 - '@storybook/core-server@8.0.9(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@storybook/core-server@8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@aw-web-design/x-default-browser': 1.4.126 '@babel/core': 7.24.5 '@discoveryjs/json-ext': 0.5.7 - '@storybook/builder-manager': 8.0.9(encoding@0.1.13) + '@storybook/builder-manager': 8.0.9 '@storybook/channels': 8.0.9 - '@storybook/core-common': 8.0.9(encoding@0.1.13) + '@storybook/core-common': 8.0.9 '@storybook/core-events': 8.0.9 '@storybook/csf': 0.1.6 '@storybook/csf-tools': 8.0.9 @@ -11814,7 +11789,7 @@ snapshots: '@storybook/manager-api': 8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/node-logger': 8.0.9 '@storybook/preview-api': 8.0.9 - '@storybook/telemetry': 8.0.9(encoding@0.1.13) + '@storybook/telemetry': 8.0.9 '@storybook/types': 8.0.9 '@types/detect-port': 1.3.5 '@types/node': 18.19.31 @@ -11877,9 +11852,9 @@ snapshots: '@storybook/docs-mdx@3.0.0': {} - '@storybook/docs-tools@8.0.9(encoding@0.1.13)': + '@storybook/docs-tools@8.0.9': dependencies: - '@storybook/core-common': 8.0.9(encoding@0.1.13) + '@storybook/core-common': 8.0.9 '@storybook/core-events': 8.0.9 '@storybook/preview-api': 8.0.9 '@storybook/types': 8.0.9 @@ -11969,13 +11944,13 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@storybook/react-vite@8.0.9(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.10(@types/node@20.11.27)(sass@1.75.0)(terser@5.31.0))': + '@storybook/react-vite@8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.10(@types/node@20.11.27)(sass@1.75.0)(terser@5.31.0))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.4.5)(vite@5.2.10(@types/node@20.11.27)(sass@1.75.0)(terser@5.31.0)) '@rollup/pluginutils': 5.1.0(rollup@4.17.2) - '@storybook/builder-vite': 8.0.9(encoding@0.1.13)(typescript@5.4.5)(vite@5.2.10(@types/node@20.11.27)(sass@1.75.0)(terser@5.31.0)) + '@storybook/builder-vite': 8.0.9(typescript@5.4.5)(vite@5.2.10(@types/node@20.11.27)(sass@1.75.0)(terser@5.31.0)) '@storybook/node-logger': 8.0.9 - '@storybook/react': 8.0.9(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.4.5) + '@storybook/react': 8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.4.5) find-up: 5.0.0 magic-string: 0.30.10 react: 18.2.0 @@ -11992,10 +11967,10 @@ snapshots: - typescript - vite-plugin-glimmerx - '@storybook/react@8.0.9(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.4.5)': + '@storybook/react@8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.4.5)': dependencies: '@storybook/client-logger': 8.0.9 - '@storybook/docs-tools': 8.0.9(encoding@0.1.13) + '@storybook/docs-tools': 8.0.9 '@storybook/global': 5.0.0 '@storybook/preview-api': 8.0.9 '@storybook/react-dom-shim': 8.0.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -12029,10 +12004,10 @@ snapshots: memoizerific: 1.11.3 qs: 6.12.1 - '@storybook/telemetry@8.0.9(encoding@0.1.13)': + '@storybook/telemetry@8.0.9': dependencies: '@storybook/client-logger': 8.0.9 - '@storybook/core-common': 8.0.9(encoding@0.1.13) + '@storybook/core-common': 8.0.9 '@storybook/csf-tools': 8.0.9 chalk: 4.1.2 detect-package-manager: 2.0.1 @@ -13606,7 +13581,7 @@ snapshots: browserslist@4.23.0: dependencies: caniuse-lite: 1.0.30001614 - electron-to-chromium: 1.4.751 + electron-to-chromium: 1.4.752 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.23.0) @@ -13910,9 +13885,9 @@ snapshots: create-require@1.1.1: {} - cross-fetch@3.1.8(encoding@0.1.13): + cross-fetch@3.1.8: dependencies: - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 transitivePeerDependencies: - encoding @@ -14422,7 +14397,7 @@ snapshots: dependencies: jake: 10.8.7 - electron-to-chromium@1.4.751: {} + electron-to-chromium@1.4.752: {} elkjs@0.8.2: {} @@ -14434,11 +14409,6 @@ snapshots: encodeurl@1.0.2: {} - encoding@0.1.13: - dependencies: - iconv-lite: 0.6.3 - optional: true - end-of-stream@1.4.4: dependencies: once: 1.4.0 @@ -14978,17 +14948,17 @@ snapshots: dependencies: reusify: 1.0.4 - fbemitter@3.0.0(encoding@0.1.13): + fbemitter@3.0.0: dependencies: - fbjs: 3.0.5(encoding@0.1.13) + fbjs: 3.0.5 transitivePeerDependencies: - encoding fbjs-css-vars@1.0.2: {} - fbjs@3.0.5(encoding@0.1.13): + fbjs@3.0.5: dependencies: - cross-fetch: 3.1.8(encoding@0.1.13) + cross-fetch: 3.1.8 fbjs-css-vars: 1.0.2 loose-envify: 1.4.0 object-assign: 4.1.1 @@ -15079,10 +15049,10 @@ snapshots: flow-parser@0.235.1: {} - flux@4.0.4(encoding@0.1.13)(react@18.2.0): + flux@4.0.4(react@18.2.0): dependencies: - fbemitter: 3.0.0(encoding@0.1.13) - fbjs: 3.0.5(encoding@0.1.13) + fbemitter: 3.0.0 + fbjs: 3.0.5 react: 18.2.0 transitivePeerDependencies: - encoding @@ -16210,11 +16180,9 @@ snapshots: node-fetch-native@1.6.4: {} - node-fetch@2.7.0(encoding@0.1.13): + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - optionalDependencies: - encoding: 0.1.13 node-releases@2.0.14: {} @@ -16475,12 +16443,7 @@ snapshots: dependencies: pixi.js: 7.4.2 - pixi-viewport@5.1.0(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/interaction@6.5.10(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/math@7.4.2)(@pixi/ticker@7.4.2)(@pixi/utils@7.4.2))(@pixi/math@7.4.2)(@pixi/ticker@7.4.2): - dependencies: - '@pixi/display': 7.4.2(@pixi/core@7.4.2) - '@pixi/interaction': 6.5.10(@pixi/core@7.4.2)(@pixi/display@7.4.2(@pixi/core@7.4.2))(@pixi/math@7.4.2)(@pixi/ticker@7.4.2)(@pixi/utils@7.4.2) - '@pixi/math': 7.4.2 - '@pixi/ticker': 7.4.2 + pixi-viewport@5.0.2: {} pixi.js@7.4.2: dependencies: @@ -16879,9 +16842,9 @@ snapshots: transitivePeerDependencies: - '@types/react' - react-json-view@1.21.3(@types/react@18.3.1)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + react-json-view@1.21.3(@types/react@18.3.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: - flux: 4.0.4(encoding@0.1.13)(react@18.2.0) + flux: 4.0.4(react@18.2.0) react: 18.2.0 react-base16-styling: 0.6.0 react-dom: 18.2.0(react@18.2.0) @@ -17437,9 +17400,9 @@ snapshots: store2@2.14.3: {} - storybook@8.0.9(@babel/preset-env@7.24.5(@babel/core@7.24.5))(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + storybook@8.0.9(@babel/preset-env@7.24.5(@babel/core@7.24.5))(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: - '@storybook/cli': 8.0.9(@babel/preset-env@7.24.5(@babel/core@7.24.5))(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@storybook/cli': 8.0.9(@babel/preset-env@7.24.5(@babel/core@7.24.5))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) transitivePeerDependencies: - '@babel/preset-env' - bufferutil