From 57b1951d0f1fee2ee71f95e36382b0402116820a Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Wed, 28 Aug 2024 12:09:10 -0400 Subject: [PATCH 01/12] feat(mapAttributeColoring): coloring based on data attributes --- .../lib/vis/visualizations/mapvis/config.ts | 5 ++ .../mapvis/layers/heatmap-layer/HeatLayer.tsx | 35 +---------- .../visualizations/mapvis/layers/index.tsx | 7 +-- .../layers/nodelink-layer/NodeLinkLayer.tsx | 61 +++++++++++++++++- .../layers/nodelink-layer/NodeLinkOptions.tsx | 63 ++++++++++++++++++- .../vis/visualizations/mapvis/mapvis.types.ts | 1 + .../lib/vis/visualizations/mapvis/utils.ts | 2 +- .../lib/vis/visualizations/mapvis/utlis.tsx | 10 --- 8 files changed, 129 insertions(+), 55 deletions(-) create mode 100644 libs/shared/lib/vis/visualizations/mapvis/config.ts delete mode 100644 libs/shared/lib/vis/visualizations/mapvis/utlis.tsx diff --git a/libs/shared/lib/vis/visualizations/mapvis/config.ts b/libs/shared/lib/vis/visualizations/mapvis/config.ts new file mode 100644 index 000000000..db5f810c6 --- /dev/null +++ b/libs/shared/lib/vis/visualizations/mapvis/config.ts @@ -0,0 +1,5 @@ +export const MAP_PROVIDER = [ + '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', +]; diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx index 52395513b..5641f0182 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx @@ -1,8 +1,6 @@ import React from 'react'; import { CompositeLayer, HeatmapLayer, Layer } from 'deck.gl'; -import * as d3 from 'd3'; -import { getDistance } from '../../utlis'; -import { CompositeLayerType, Edge, LayerProps } from '../../mapvis.types'; +import { CompositeLayerType, LayerProps } from '../../mapvis.types'; import { Node } from '@graphpolaris/shared/lib/data-access'; export class HeatLayer extends CompositeLayer<CompositeLayerType> { @@ -17,37 +15,6 @@ export class HeatLayer extends CompositeLayer<CompositeLayerType> { return changeFlags.propsOrDataChanged || changeFlags.somethingChanged; } - createSegments(edges: Edge[]) { - // Generates a path between source and target nodes - return edges.map((edge: Edge, index) => { - const length = getDistance(edge.path[0], edge.path[1]); - const nSegments = length * this.props.settings.nSegments; - - let xscale = d3 - .scaleLinear() - .domain([0, nSegments + 1]) - .range([edge.path[0][0], edge.path[1][0]]); - - let yscale = d3 - .scaleLinear() - .domain([0, nSegments + 1]) - .range([edge.path[0][1], edge.path[1][1]]); - - let source = edge.path[0]; - let target = null; - let local = [source]; - - for (let j = 1; j <= nSegments; j++) { - target = [xscale(j), yscale(j)]; - local.push(target); - source = target; - } - - local.push(edge.path.slice(-1)[0]); - return { ...edge, path: local }; - }); - } - renderLayers() { const { data, settings, getNodeLocation, setLayerIds, graphMetadata } = this.props; const layerSettings = settings[HeatLayer.type]; diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx index 9e5f7c31e..92360935c 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx @@ -7,6 +7,7 @@ import { ChoroplethOptions } from './choropleth-layer/ChoroplethOptions'; import { TileLayer, BitmapLayer } from 'deck.gl'; import { MapProps } from '../mapvis'; import { LayerSettingsComponentType } from '../mapvis.types'; +import { MAP_PROVIDER } from '../config'; export type LayerTypes = 'nodelink' | 'heatmap' | 'choropleth'; @@ -26,12 +27,6 @@ export const layerSettings: Record<string, React.FC<MapLayerSettingsPropTypes>> choropleth: ChoroplethOptions, }; -const MAP_PROVIDER = [ - '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', -]; - export const createBaseMap = () => { return new TileLayer({ data: MAP_PROVIDER, diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx index dd407682b..48a70c7b7 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx @@ -3,19 +3,63 @@ import { CompositeLayer, Layer } from 'deck.gl'; import { LineLayer, ScatterplotLayer, TextLayer } from '@deck.gl/layers'; import { CompositeLayerType, LayerProps } from '../../mapvis.types'; import { BrushingExtension, CollisionFilterExtension } from '@deck.gl/extensions'; +import { scaleLinear, ScaleLinear, color, interpolateRgb } from 'd3'; + +interface ColorScales { + [key: string]: ScaleLinear<string, string>; +} + +interface NodeLinkLayerState { + colorScales: ColorScales; + [key: string]: any; +} export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { static type = 'nodelink'; private _layers: Record<string, Layer> = {}; + state: NodeLinkLayerState = { + colorScales: {}, + }; + constructor(props: LayerProps) { super(props); } - updateState({ changeFlags }: { changeFlags: any }) { + updateState({ props, changeFlags }: { props: any; changeFlags: any }) { + if (changeFlags.propsOrDataChanged) { + const colorScales: ColorScales = {}; + Object.keys(props.settings[NodeLinkLayer.type].nodes).map((label) => { + const nodeSettings = props.settings[NodeLinkLayer.type].nodes[label]; + const nodeDistribution = props.graphMetadata.nodes.types[label].attributes; + if (nodeSettings.colorByAttribute) { + if (nodeSettings.colorAttributeType === 'numerical') { + colorScales[label] = this.setNumericalColor( + nodeDistribution[nodeSettings?.colorAttribute]?.min, + nodeDistribution[nodeSettings?.colorAttribute]?.max, + nodeSettings.colorScale, + ); + } + } + }); + this.setState({ colorScales }); + } return changeFlags.propsOrDataChanged || changeFlags.somethingChanged; } + setNumericalColor(min: number, max: number, colorScale: string) { + return scaleLinear<string>().domain([min, max]).range(['white', colorScale]).interpolate(interpolateRgb); + } + + rgbStringToArray(rgbString: string): [number, number, number] { + const rgb = color(rgbString); + if (!rgb || !rgb.formatRgb()) return [0, 0, 0]; + const match = rgb.formatRgb().match(/\d+/g); + if (!match || match.length !== 3) return [0, 0, 0]; + const [r, g, b] = match.map(Number) as [number, number, number]; + return [r, g, b]; + } + renderLayers() { const { data, settings, getNodeLocation, ml, graphMetadata, selected } = this.props; const layerSettings = settings[NodeLinkLayer.type]; @@ -77,7 +121,18 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { visible: !layerSettings?.nodes[label]?.hidden, data: nodes, pickable: true, - getFillColor: (d) => layerSettings?.nodes[label]?.color, + getFillColor: (d) => { + if (layerSettings?.nodes[label].colorByAttribute) { + const attributeValue = d.attributes[layerSettings?.nodes[label].colorAttribute]; + if (layerSettings?.nodes[label].colorAttributeType === 'categorical') { + return layerSettings?.nodes[label]?.colorMapping[attributeValue]; + } else if (layerSettings?.nodes[label].colorAttributeType === 'numerical') { + const colorScale = this.state.colorScales[label]; + return this.rgbStringToArray(colorScale(attributeValue)); + } + } + return layerSettings?.nodes[label].color; + }, getPosition: (d) => getNodeLocation(d._id), getRadius: (d) => layerSettings?.nodes[label]?.size, radiusMinPixels: 5, @@ -86,6 +141,8 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { stroked: true, updateTriggers: { getIcon: [selected], + getRadius: [layerSettings?.nodes[label].size], + getFillColor: [this.state.colorScales], }, }); diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index 566817294..8d09ede74 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -3,7 +3,7 @@ import ColorPicker from '@graphpolaris/shared/lib/components/colorComponents/col import { Button, DropdownColorLegend, EntityPill, Icon, Input, RelationPill } from '@graphpolaris/shared/lib/components'; import { MapProps } from '../../mapvis'; import { LayerSettingsComponentType } from '../../mapvis.types'; -import { nodeColorHex } from '../../utils'; +import { nodeColorRGB } from '../../utils'; const defaultNodeSettings = (index: number) => ({ colorByAttribute: false, @@ -11,7 +11,7 @@ const defaultNodeSettings = (index: number) => ({ colorAttributeType: undefined, hidden: false, shape: 'circle', - color: nodeColorHex(index), + color: nodeColorRGB(index), size: 40, }); @@ -222,6 +222,65 @@ export function NodeLinkOptions({ }) } /> + {nodeSettings.colorByAttribute && ( + <div> + <Input + inline + label="Color based on" + type="dropdown" + value={nodeSettings.colorAttribute} + options={Object.keys(graphMetadata.nodes.types[nodeType]?.attributes)} + onChange={(val) => + updateLayerSettings({ + nodes: { + ...layerSettings.nodes, + [nodeType]: { + ...nodeSettings, + colorAttribute: String(val), + colorAttributeType: graphMetadata.nodes.types[nodeType].attributes[val].dimension, + }, + }, + }) + } + /> + {nodeSettings.colorAttributeType === 'numerical' ? ( + <div> + <p>Select color scale:</p> + <DropdownColorLegend + value={settings?.colorScale} + onChange={(val) => + updateLayerSettings({ + nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, colorScale: val } }, + }) + } + /> + </div> + ) : nodeSettings.colorAttributeType === 'categorical' ? ( + <div> + {graphMetadata.nodes.types[nodeType]?.attributes?.[nodeSettings?.colorAttribute]?.values.map( + (attr: string) => ( + <div key={attr} className="flex items-center justify-between"> + <p>{attr}</p> + <ColorPicker + value={(nodeSettings?.colorMapping ?? {})[attr] ?? [0, 0, 0]} + updateValue={(val) => { + updateLayerSettings({ + nodes: { + ...layerSettings.nodes, + [nodeType]: { ...nodeSettings, colorMapping: { ...nodeSettings.colorMapping, [attr]: val } }, + }, + }); + }} + /> + </div> + ), + )} + </div> + ) : ( + <div>Something went wrong</div> + )} + </div> + )} </div> </div> )} diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts index 08ef8bd7a..6c26d6134 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts +++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts @@ -25,6 +25,7 @@ export type MapNodeData = { colorAttribute?: string | undefined; colorAttributeType?: string | undefined; colorScale: string; + colorMapping?: { [id: string]: number[] }; }; export type MapEdgeData = { diff --git a/libs/shared/lib/vis/visualizations/mapvis/utils.ts b/libs/shared/lib/vis/visualizations/mapvis/utils.ts index d45003bd9..3593d2450 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/utils.ts +++ b/libs/shared/lib/vis/visualizations/mapvis/utils.ts @@ -1,6 +1,6 @@ import { visualizationColors } from 'config'; -export function nodeColorHex(num: number) { +export function nodeColorRGB(num: number) { const colorVal = visualizationColors.GPCat.colors[14][num % visualizationColors.GPCat.colors[14].length]; const hex = colorVal.replace(/^#/, ''); const r = parseInt(hex.substring(0, 2), 16); diff --git a/libs/shared/lib/vis/visualizations/mapvis/utlis.tsx b/libs/shared/lib/vis/visualizations/mapvis/utlis.tsx deleted file mode 100644 index 06db43a10..000000000 --- a/libs/shared/lib/vis/visualizations/mapvis/utlis.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { Coordinate } from './mapvis.types'; - -export const getDistance = (loc1: Coordinate, loc2: Coordinate): number => { - const [minLon, minLat]: Coordinate = loc1; - const [maxLon, maxLat]: Coordinate = loc2; - const lonDistance: number = (maxLon ?? 0) - (minLon ?? 0); - const latDistance: number = (maxLat ?? 0) - (minLat ?? 0); - const featureHypot: number = Math.hypot(lonDistance, latDistance); - return featureHypot; -}; -- GitLab From 19db071d963d33eb90b05e342b52120d8d9b6688 Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Fri, 30 Aug 2024 09:12:30 -0400 Subject: [PATCH 02/12] feat(mapAttributeColoring): hidden nodes and parse integers --- .../layers/nodelink-layer/NodeLinkLayer.tsx | 14 ++++++++--- .../layers/nodelink-layer/NodeLinkOptions.tsx | 24 +++++++++++-------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx index 48a70c7b7..13303880e 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx @@ -75,13 +75,15 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { return acc; }, {}); + const hiddenNodes = new Set(data.nodes.filter((node) => layerSettings.nodes[node.label]?.hidden).map((node) => node._id)); + graphMetadata.edges.labels.forEach((label: string) => { const layerId = `${label}-edges-line`; - const edgeData = data.edges.filter((edge: any) => { + const edgeData = data.edges.filter((edge) => { const from = nodeLocations[edge.from]; const to = nodeLocations[edge.to]; - return from && to; + return from && to && !hiddenNodes.has(edge.from) && !hiddenNodes.has(edge.to); }); this._layers[layerId] = new LineLayer({ @@ -123,10 +125,16 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { pickable: true, getFillColor: (d) => { if (layerSettings?.nodes[label].colorByAttribute) { - const attributeValue = d.attributes[layerSettings?.nodes[label].colorAttribute]; + let attributeValue = d.attributes[layerSettings?.nodes[label].colorAttribute]; if (layerSettings?.nodes[label].colorAttributeType === 'categorical') { return layerSettings?.nodes[label]?.colorMapping[attributeValue]; } else if (layerSettings?.nodes[label].colorAttributeType === 'numerical') { + if (typeof attributeValue === 'string') { + const numericValue = parseFloat(attributeValue.replace(/[^0-9.]/g, '')); + if (!isNaN(numericValue)) { + attributeValue = numericValue; + } + } const colorScale = this.state.colorScales[label]; return this.rgbStringToArray(colorScale(attributeValue)); } diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index 8d09ede74..b91ed9da1 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -255,10 +255,13 @@ export function NodeLinkOptions({ } /> </div> - ) : nodeSettings.colorAttributeType === 'categorical' ? ( - <div> - {graphMetadata.nodes.types[nodeType]?.attributes?.[nodeSettings?.colorAttribute]?.values.map( - (attr: string) => ( + ) : ( + nodeSettings.colorAttributeType === 'categorical' && + nodeSettings.colorAttribute && ( + <div> + {( + graphMetadata.nodes.types[nodeType] as { attributes: { [key: string]: { values: string[] } } } + )?.attributes?.[nodeSettings.colorAttribute]?.values.map((attr: string) => ( <div key={attr} className="flex items-center justify-between"> <p>{attr}</p> <ColorPicker @@ -267,17 +270,18 @@ export function NodeLinkOptions({ updateLayerSettings({ nodes: { ...layerSettings.nodes, - [nodeType]: { ...nodeSettings, colorMapping: { ...nodeSettings.colorMapping, [attr]: val } }, + [nodeType]: { + ...nodeSettings, + colorMapping: { ...nodeSettings.colorMapping, [attr]: val }, + }, }, }); }} /> </div> - ), - )} - </div> - ) : ( - <div>Something went wrong</div> + ))} + </div> + ) )} </div> )} -- GitLab From c9369b2ce1ed8d62615eed7544a3d8fc5cd98452 Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Wed, 28 Aug 2024 12:09:10 -0400 Subject: [PATCH 03/12] feat(mapAttributeColoring): coloring based on data attributes --- .../layers/nodelink-layer/NodeLinkOptions.tsx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index b91ed9da1..8b2f28e7e 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -255,6 +255,27 @@ export function NodeLinkOptions({ } /> </div> + ) : nodeSettings.colorAttributeType === 'categorical' ? ( + <div> + {graphMetadata.nodes.types[nodeType]?.attributes?.[nodeSettings?.colorAttribute]?.values.map( + (attr: string) => ( + <div key={attr} className="flex items-center justify-between"> + <p>{attr}</p> + <ColorPicker + value={(nodeSettings?.colorMapping ?? {})[attr] ?? [0, 0, 0]} + updateValue={(val) => { + updateLayerSettings({ + nodes: { + ...layerSettings.nodes, + [nodeType]: { ...nodeSettings, colorMapping: { ...nodeSettings.colorMapping, [attr]: val } }, + }, + }); + }} + /> + </div> + ), + )} + </div> ) : ( nodeSettings.colorAttributeType === 'categorical' && nodeSettings.colorAttribute && ( -- GitLab From ad603aaa4f2693f5ab8670283077f49f0755e841 Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Fri, 30 Aug 2024 09:12:30 -0400 Subject: [PATCH 04/12] feat(mapAttributeColoring): hidden nodes and parse integers --- .../layers/nodelink-layer/NodeLinkOptions.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index 8b2f28e7e..956812676 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -255,10 +255,13 @@ export function NodeLinkOptions({ } /> </div> - ) : nodeSettings.colorAttributeType === 'categorical' ? ( - <div> - {graphMetadata.nodes.types[nodeType]?.attributes?.[nodeSettings?.colorAttribute]?.values.map( - (attr: string) => ( + ) : ( + nodeSettings.colorAttributeType === 'categorical' && + nodeSettings.colorAttribute && ( + <div> + {( + graphMetadata.nodes.types[nodeType] as { attributes: { [key: string]: { values: string[] } } } + )?.attributes?.[nodeSettings.colorAttribute]?.values.map((attr: string) => ( <div key={attr} className="flex items-center justify-between"> <p>{attr}</p> <ColorPicker @@ -267,7 +270,10 @@ export function NodeLinkOptions({ updateLayerSettings({ nodes: { ...layerSettings.nodes, - [nodeType]: { ...nodeSettings, colorMapping: { ...nodeSettings.colorMapping, [attr]: val } }, + [nodeType]: { + ...nodeSettings, + colorMapping: { ...nodeSettings.colorMapping, [attr]: val }, + }, }, }); }} -- GitLab From e1d7c5aaf74b07eb594215b93874908c0e409cd6 Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Mon, 2 Sep 2024 09:22:53 -0400 Subject: [PATCH 05/12] feat(mapAttributeColoring): fixed rebase issue --- .../layers/nodelink-layer/NodeLinkOptions.tsx | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index 956812676..b91ed9da1 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -255,33 +255,6 @@ export function NodeLinkOptions({ } /> </div> - ) : ( - nodeSettings.colorAttributeType === 'categorical' && - nodeSettings.colorAttribute && ( - <div> - {( - graphMetadata.nodes.types[nodeType] as { attributes: { [key: string]: { values: string[] } } } - )?.attributes?.[nodeSettings.colorAttribute]?.values.map((attr: string) => ( - <div key={attr} className="flex items-center justify-between"> - <p>{attr}</p> - <ColorPicker - value={(nodeSettings?.colorMapping ?? {})[attr] ?? [0, 0, 0]} - updateValue={(val) => { - updateLayerSettings({ - nodes: { - ...layerSettings.nodes, - [nodeType]: { - ...nodeSettings, - colorMapping: { ...nodeSettings.colorMapping, [attr]: val }, - }, - }, - }); - }} - /> - </div> - ), - )} - </div> ) : ( nodeSettings.colorAttributeType === 'categorical' && nodeSettings.colorAttribute && ( -- GitLab From fd3bf741eb6334ec0d7a7e48b6ccebc9e6c2d78d Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Mon, 2 Sep 2024 10:05:09 -0400 Subject: [PATCH 06/12] feat(mapAttributeColoring): use of new tooltip style --- .../mapvis/components/Tooltip.tsx | 67 ++++++++++--------- .../layers/nodelink-layer/NodeLinkOptions.tsx | 2 +- .../lib/vis/visualizations/mapvis/mapvis.tsx | 22 +++++- .../lib/vis/visualizations/mapvis/utils.ts | 7 ++ 4 files changed, 63 insertions(+), 35 deletions(-) diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/Tooltip.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/Tooltip.tsx index 772e9ea72..ea5bc92fc 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/components/Tooltip.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/Tooltip.tsx @@ -2,11 +2,11 @@ import React from 'react'; import { NodeType } from '../../nodelinkvis/types'; import { GeoJsonType } from '../mapvis.types'; import { SearchResultType } from '../mapvis.types'; +import { TooltipProvider } from '@graphpolaris/shared/lib/components'; export type NodelinkPopupProps = { type: 'node' | 'area' | 'location'; data: NodeType | GeoJsonType | SearchResultType; - onClose: () => void; }; const isGeoJsonType = (data: NodeType | GeoJsonType | SearchResultType): data is GeoJsonType => { @@ -18,15 +18,30 @@ export const MapTooltip = (props: NodelinkPopupProps) => { const renderNodeDetails = (node: NodeType) => ( <div> - {node.attributes && + {Object.keys(node.attributes).length === 0 ? ( + <div className="flex justify-center items-center h-full"> + <span>No attributes</span> + </div> + ) : ( Object.entries(node.attributes).map(([k, v]) => ( - <div key={k} className="flex flex-row gap-3"> - <span>{k}: </span> - <span className="ml-auto max-w-[10rem] text-right truncate"> - <span title={JSON.stringify(v)}>{JSON.stringify(v)}</span> + <div key={k} className="flex flex-row gap-1 items-center min-h-5"> + <span className="font-semibold truncate min-w-[40%]">{k}</span> + <span className="ml-auto text-right truncate grow-1 flex items-center"> + {v !== undefined && (typeof v !== 'object' || Array.isArray(v)) && v != '' ? ( + <span className="ml-auto text-right truncate">{typeof v === 'number' ? v.toLocaleString('de-DE') : v.toString()}</span> + ) : ( + <div + className={`ml-auto mt-auto h-4 w-12 border-[1px] solid border-gray`} + style={{ + background: + 'repeating-linear-gradient(-45deg, transparent, transparent 6px, #eaeaea 6px, #eaeaea 8px), linear-gradient(to bottom, transparent, transparent)', + }} + ></div> + )} </span> </div> - ))} + )) + )} </div> ); @@ -79,32 +94,22 @@ export const MapTooltip = (props: NodelinkPopupProps) => { ); return ( - <div className="text-[0.9rem] min-w-[10rem]"> - <div className="card-body p-0"> - <span className="px-2.5 pt-2"> - <span>{type === 'node' ? 'Node' : 'Area'}</span> - <span className="float-right"> + <TooltipProvider delayDuration={100}> + <div className="text-[0.9rem] min-w-[10rem]"> + <div className="card-body p-0"> + <div className="h-[1px] w-full bg-secondary-200"></div> + <div className="px-2.5 text-[0.8rem]"> {type === 'node' - ? (data as NodeType)?._id - : isGeoJsonType(data) - ? data.properties?.name - : type === 'location' - ? (data as SearchResultType)?.name - : 'N/A'} - </span> - </span> - <div className="h-[1px] w-full bg-secondary-200"></div> - <div className="px-2.5 text-[0.8rem]"> - {type === 'node' - ? data && 'attributes' in data - ? renderNodeDetails(data as NodeType) - : null - : data && isGeoJsonType(data) - ? renderAreaDetails(data as GeoJsonType) - : renderLocationDetails(data as SearchResultType)} + ? data && 'attributes' in data + ? renderNodeDetails(data as NodeType) + : null + : data && isGeoJsonType(data) + ? renderAreaDetails(data as GeoJsonType) + : renderLocationDetails(data as SearchResultType)} + </div> + <div className="h-[1px] w-full"></div> </div> - <div className="h-[1px] w-full"></div> </div> - </div> + </TooltipProvider> ); }; diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index b91ed9da1..544e05b76 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -263,7 +263,7 @@ export function NodeLinkOptions({ graphMetadata.nodes.types[nodeType] as { attributes: { [key: string]: { values: string[] } } } )?.attributes?.[nodeSettings.colorAttribute]?.values.map((attr: string) => ( <div key={attr} className="flex items-center justify-between"> - <p>{attr}</p> + <p className="truncate w-18">{attr}</p> <ColorPicker value={(nodeSettings?.colorMapping ?? {})[attr] ?? [0, 0, 0]} updateValue={(val) => { diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx index e48b49d6f..453ca66ce 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx @@ -7,8 +7,11 @@ import { layerTypes, createBaseMap, LayerTypes } from './layers'; import { Node } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice'; import { geoCentroid } from 'd3'; import { Attribution, ActionBar, MapTooltip, MapSettings } from './components'; -import { Tooltip, TooltipContent, TooltipTrigger } from '@graphpolaris/shared/lib/components'; import { useSelectionLayer, useCoordinateLookup } from './hooks'; +import { VisualizationTooltip } from '@graphpolaris/shared/lib/components/VisualizationTooltip'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@graphpolaris/shared/lib/components/tooltip'; +import { isGeoJsonType } from './utils'; +import { NodeType } from '../nodelinkvis/types'; export type MapProps = { layer: LayerTypes; @@ -257,7 +260,20 @@ export const MapVis = forwardRef((props: VisualizationPropTypes<MapProps>, refEx <Tooltip key={index} open={true} interactive={false} boundaryElement={ref} showArrow={true}> <TooltipTrigger x={node.x} y={node.y} /> <TooltipContent> - <MapTooltip type={node.selectedType} onClose={() => {}} data={{ ...node }} key={node._id} /> + <VisualizationTooltip + name={ + node.selectedType === 'node' + ? (node as NodeType)?._id + : isGeoJsonType(node) + ? node.properties?.name + : node.selectedType === 'location' + ? (node as SearchResultType)?.name + : 'N/A' + } + colorHeader="#FB9637" + > + <MapTooltip type={node.selectedType} data={{ ...node }} key={node._id} /> + </VisualizationTooltip> </TooltipContent> </Tooltip> ))} @@ -265,7 +281,7 @@ export const MapVis = forwardRef((props: VisualizationPropTypes<MapProps>, refEx <Tooltip open={true} interactive={false} boundaryElement={ref} showArrow={true}> <TooltipTrigger x={searchResult.x} y={searchResult.y} /> <TooltipContent> - <MapTooltip type="location" onClose={() => {}} data={{ ...searchResult }} key={searchResult.name} /> + <MapTooltip type="location" data={{ ...searchResult }} key={searchResult.name} /> </TooltipContent> </Tooltip> )} diff --git a/libs/shared/lib/vis/visualizations/mapvis/utils.ts b/libs/shared/lib/vis/visualizations/mapvis/utils.ts index 3593d2450..9cd704f2a 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/utils.ts +++ b/libs/shared/lib/vis/visualizations/mapvis/utils.ts @@ -1,4 +1,7 @@ import { visualizationColors } from 'config'; +import { NodeType } from '../nodelinkvis/types'; +import { GeoJsonType } from './mapvis.types'; +import { SearchResultType } from './mapvis.types'; export function nodeColorRGB(num: number) { const colorVal = visualizationColors.GPCat.colors[14][num % visualizationColors.GPCat.colors[14].length]; @@ -8,3 +11,7 @@ export function nodeColorRGB(num: number) { const b = parseInt(hex.substring(4, 6), 16); return [r, g, b]; } + +export const isGeoJsonType = (data: NodeType | GeoJsonType | SearchResultType): data is GeoJsonType => { + return (data as GeoJsonType).properties !== undefined; +}; -- GitLab From 772124367d1b31a8bea1c66517fa353716e5aabc Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Mon, 2 Sep 2024 10:16:26 -0400 Subject: [PATCH 07/12] feat(mapAttributeColoring): fixed numerical color picker --- .../colorComponents/colorDropdown/index.tsx | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx index f16a9a350..1ffe9ed53 100644 --- a/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx +++ b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx @@ -51,7 +51,7 @@ export const DropdownColorLegend = ({ value, onChange }: DropdownColorLegendProp return ( <div className="w-200 h-200 relative"> - <DropdownContainer> + <DropdownContainer open={menuOpen}> <DropdownTrigger title={ <div className="flex items-center h-4"> @@ -69,23 +69,21 @@ export const DropdownColorLegend = ({ value, onChange }: DropdownColorLegendProp } onClick={() => setMenuOpen(!menuOpen)} /> - {menuOpen && ( - <DropdownItemContainer className="absolute w-60 bg-white shadow-lg z-10"> - <ul> - {Object.keys(colorStructure).map((option: string, index) => ( - <li key={index} onClick={() => handleOptionClick(option)} className="cursor-pointer flex items-center ml-2 h-4 m-2"> - <ColorLegend - key={index.toString() + '_colorLegend'} - colors={colorStructure[option].colors} - data={colorStructure[option].data} - name={colorStructure[option].name} - showAxis={colorStructure[option].showAxis} - /> - </li> - ))} - </ul> - </DropdownItemContainer> - )} + <DropdownItemContainer className="absolute w-60 bg-white shadow-lg z-10"> + <ul> + {Object.keys(colorStructure).map((option: string, index) => ( + <li key={index} onClick={() => handleOptionClick(option)} className="cursor-pointer flex items-center ml-2 h-4 m-2"> + <ColorLegend + key={index.toString() + '_colorLegend'} + colors={colorStructure[option].colors} + data={colorStructure[option].data} + name={colorStructure[option].name} + showAxis={colorStructure[option].showAxis} + /> + </li> + ))} + </ul> + </DropdownItemContainer> </DropdownContainer> </div> ); -- GitLab From 07e8e9fc648f7382b91ba21b55688009b0f7551f Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Mon, 2 Sep 2024 10:23:07 -0400 Subject: [PATCH 08/12] feat(mapAttributeColoring): handle empty category in attributes --- .../mapvis/layers/nodelink-layer/NodeLinkOptions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index 544e05b76..3266577c2 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -263,7 +263,7 @@ export function NodeLinkOptions({ graphMetadata.nodes.types[nodeType] as { attributes: { [key: string]: { values: string[] } } } )?.attributes?.[nodeSettings.colorAttribute]?.values.map((attr: string) => ( <div key={attr} className="flex items-center justify-between"> - <p className="truncate w-18">{attr}</p> + <p className="truncate w-18">{attr.length > 0 ? attr : 'Empty val'}</p> <ColorPicker value={(nodeSettings?.colorMapping ?? {})[attr] ?? [0, 0, 0]} updateValue={(val) => { -- GitLab From 98f59f14964ddfbbaff0ec33c4a07622eec02880 Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Wed, 4 Sep 2024 08:48:24 -0400 Subject: [PATCH 09/12] feat(mapAttributeColoring): rebasing --- .../layers/nodelink-layer/NodeLinkOptions.tsx | 96 ++++++------------- 1 file changed, 30 insertions(+), 66 deletions(-) diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index 3266577c2..ff66a93e5 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -6,12 +6,14 @@ import { LayerSettingsComponentType } from '../../mapvis.types'; import { nodeColorRGB } from '../../utils'; const defaultNodeSettings = (index: number) => ({ + color: nodeColorRGB(index), + colorMapping: {}, + colorScale: undefined, colorByAttribute: false, colorAttribute: undefined, colorAttributeType: undefined, hidden: false, shape: 'circle', - color: nodeColorRGB(index), size: 40, }); @@ -161,7 +163,6 @@ export function NodeLinkOptions({ type="dropdown" value={nodeSettings.colorAttribute} options={Object.keys(graphMetadata.nodes.types[nodeType]?.attributes)} - disabled={!settings.nodes} onChange={(val) => updateLayerSettings({ nodes: { @@ -177,6 +178,7 @@ export function NodeLinkOptions({ /> {nodeSettings.colorAttributeType === 'numerical' ? ( <div> + <p>Select color scale:</p> <DropdownColorLegend value={settings?.colorScale} onChange={(val) => @@ -187,7 +189,32 @@ export function NodeLinkOptions({ /> </div> ) : ( - <div>Categorical</div> + nodeSettings.colorAttributeType === 'categorical' && + nodeSettings.colorAttribute && ( + <div> + {( + graphMetadata.nodes.types[nodeType] as { attributes: { [key: string]: { values: string[] } } } + )?.attributes?.[nodeSettings.colorAttribute]?.values.map((attr: string) => ( + <div key={attr} className="flex items-center justify-between"> + <p className="truncate w-18">{attr.length > 0 ? attr : 'Empty val'}</p> + <ColorPicker + value={(nodeSettings?.colorMapping ?? {})[attr] ?? [0, 0, 0]} + updateValue={(val) => { + updateLayerSettings({ + nodes: { + ...layerSettings.nodes, + [nodeType]: { + ...nodeSettings, + colorMapping: { ...nodeSettings.colorMapping, [attr]: val }, + }, + }, + }); + }} + /> + </div> + ))} + </div> + ) )} </div> )} @@ -222,69 +249,6 @@ export function NodeLinkOptions({ }) } /> - {nodeSettings.colorByAttribute && ( - <div> - <Input - inline - label="Color based on" - type="dropdown" - value={nodeSettings.colorAttribute} - options={Object.keys(graphMetadata.nodes.types[nodeType]?.attributes)} - onChange={(val) => - updateLayerSettings({ - nodes: { - ...layerSettings.nodes, - [nodeType]: { - ...nodeSettings, - colorAttribute: String(val), - colorAttributeType: graphMetadata.nodes.types[nodeType].attributes[val].dimension, - }, - }, - }) - } - /> - {nodeSettings.colorAttributeType === 'numerical' ? ( - <div> - <p>Select color scale:</p> - <DropdownColorLegend - value={settings?.colorScale} - onChange={(val) => - updateLayerSettings({ - nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, colorScale: val } }, - }) - } - /> - </div> - ) : ( - nodeSettings.colorAttributeType === 'categorical' && - nodeSettings.colorAttribute && ( - <div> - {( - graphMetadata.nodes.types[nodeType] as { attributes: { [key: string]: { values: string[] } } } - )?.attributes?.[nodeSettings.colorAttribute]?.values.map((attr: string) => ( - <div key={attr} className="flex items-center justify-between"> - <p className="truncate w-18">{attr.length > 0 ? attr : 'Empty val'}</p> - <ColorPicker - value={(nodeSettings?.colorMapping ?? {})[attr] ?? [0, 0, 0]} - updateValue={(val) => { - updateLayerSettings({ - nodes: { - ...layerSettings.nodes, - [nodeType]: { - ...nodeSettings, - colorMapping: { ...nodeSettings.colorMapping, [attr]: val }, - }, - }, - }); - }} - /> - </div> - ))} - </div> - ) - )} - </div> - )} </div> </div> )} -- GitLab From 41e694dfa54cd63fcbd9478d0121e438e7ee2338 Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Wed, 4 Sep 2024 08:50:43 -0400 Subject: [PATCH 10/12] feat(mapAttributeColoring): rebasing --- .../mapvis/layers/nodelink-layer/NodeLinkOptions.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index ff66a93e5..3389f87a6 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -178,6 +178,7 @@ export function NodeLinkOptions({ /> {nodeSettings.colorAttributeType === 'numerical' ? ( <div> + <p>Select color scale:</p> <p>Select color scale:</p> <DropdownColorLegend value={settings?.colorScale} -- GitLab From 5e3a86f0f47889aa78a123e7d8dcc3bed6e5b8c4 Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Wed, 4 Sep 2024 08:52:59 -0400 Subject: [PATCH 11/12] feat(mapAttributeColoring): fixed double p tag issue --- .../mapvis/layers/nodelink-layer/NodeLinkOptions.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index 3389f87a6..ff66a93e5 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -178,7 +178,6 @@ export function NodeLinkOptions({ /> {nodeSettings.colorAttributeType === 'numerical' ? ( <div> - <p>Select color scale:</p> <p>Select color scale:</p> <DropdownColorLegend value={settings?.colorScale} -- GitLab From 33e3d99736b2a340436a76ed3591a87b8390efc3 Mon Sep 17 00:00:00 2001 From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl> Date: Thu, 5 Sep 2024 09:38:14 -0400 Subject: [PATCH 12/12] feat(mapAttributeColoring): fixed merge comments --- .../layers/nodelink-layer/NodeLinkLayer.tsx | 22 ++++++++++--------- .../layers/nodelink-layer/NodeLinkOptions.tsx | 3 ++- .../lib/vis/visualizations/mapvis/mapvis.tsx | 2 +- .../vis/visualizations/mapvis/mapvis.types.ts | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx index 13303880e..a853da41a 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx @@ -1,12 +1,13 @@ import React from 'react'; import { CompositeLayer, Layer } from 'deck.gl'; import { LineLayer, ScatterplotLayer, TextLayer } from '@deck.gl/layers'; -import { CompositeLayerType, LayerProps } from '../../mapvis.types'; +import { CompositeLayerType, Coordinate, LayerProps } from '../../mapvis.types'; import { BrushingExtension, CollisionFilterExtension } from '@deck.gl/extensions'; import { scaleLinear, ScaleLinear, color, interpolateRgb } from 'd3'; +import { Node } from '@graphpolaris/shared/lib/data-access'; interface ColorScales { - [key: string]: ScaleLinear<string, string>; + [label: string]: ScaleLinear<string, string>; } interface NodeLinkLayerState { @@ -27,6 +28,7 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { } updateState({ props, changeFlags }: { props: any; changeFlags: any }) { + // TODO: Remove any here if (changeFlags.propsOrDataChanged) { const colorScales: ColorScales = {}; Object.keys(props.settings[NodeLinkLayer.type].nodes).map((label) => { @@ -67,7 +69,7 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { const brushingExtension = new BrushingExtension(); const collisionFilter = new CollisionFilterExtension(); - const nodeLocations = data.nodes.reduce((acc: Record<string, [number, number]>, node: any) => { + const nodeLocations = data.nodes.reduce((acc: Record<string, Coordinate>, node: Node) => { const pos = getNodeLocation(node._id); if (pos && (pos[0] !== 0 || pos[1] !== 0)) { acc[node._id] = pos; @@ -116,7 +118,7 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { const layerId = `${label}-nodes-scatterplot`; const textLayerId = `${label}-label-target`; - const nodes = data.nodes.filter((node: any) => nodeLocations[node._id] && node.label === label); + const nodes = data.nodes.filter((node: Node) => nodeLocations[node._id] && node.label === label); this._layers[layerId] = new ScatterplotLayer({ id: layerId, @@ -141,10 +143,10 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { } return layerSettings?.nodes[label].color; }, - getPosition: (d) => getNodeLocation(d._id), - getRadius: (d) => layerSettings?.nodes[label]?.size, + getPosition: (d: Node) => getNodeLocation(d._id), + getRadius: () => layerSettings?.nodes[label]?.size, radiusMinPixels: 5, - getLineWidth: (d: any) => (selected && selected.some((sel) => sel._id === d._id) ? 2 : 1), + getLineWidth: (d: Node) => (selected && selected.some((sel) => sel._id === d._id) ? 2 : 1), lineWidthUnits: 'pixels', stroked: true, updateTriggers: { @@ -157,9 +159,9 @@ export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> { this._layers[textLayerId] = new TextLayer({ id: textLayerId, data: nodes, - getPosition: (d: any) => getNodeLocation(d._id), - getText: (d: any) => d.label, - getSize: (d: any) => (layerSettings?.nodes[label]?.size * 2) / d.label.length, + getPosition: (d: Node) => getNodeLocation(d._id), + getText: (d: Node) => d.label, + getSize: (d: Node) => (layerSettings?.nodes[label]?.size * 2) / d.label.length, getAlignmentBaseline: 'center', getRadius: 10, radiusScale: 20, diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index ff66a93e5..70b08dd09 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -4,6 +4,7 @@ import { Button, DropdownColorLegend, EntityPill, Icon, Input, RelationPill } fr import { MapProps } from '../../mapvis'; import { LayerSettingsComponentType } from '../../mapvis.types'; import { nodeColorRGB } from '../../utils'; +import { isEqual } from 'lodash-es'; const defaultNodeSettings = (index: number) => ({ color: nodeColorRGB(index), @@ -55,7 +56,7 @@ export function NodeLinkOptions({ {} as typeof edges, ); - if (JSON.stringify(newNodes) !== JSON.stringify(nodes) || JSON.stringify(newEdges) !== JSON.stringify(edges)) { + if (!isEqual(newNodes, nodes) || !isEqual(newEdges, edges)) { updateLayerSettings({ ...layerSettings, nodes: newNodes, diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx index 453ca66ce..c7e6c50ba 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useCallback, useState, useRef, forwardRef, useImperativeHandle } from 'react'; import DeckGL, { DeckGLProps, DeckGLRef } from '@deck.gl/react'; -import { CompositeLayer, FlyToInterpolator, MapViewState, MapViewState, WebMercatorViewport } from '@deck.gl/core'; +import { CompositeLayer, FlyToInterpolator, MapViewState, WebMercatorViewport } from '@deck.gl/core'; import { CompositeLayerType, Coordinate, LayerSettingsType, LocationInfo, SearchResultType } from './mapvis.types'; import { VISComponentType, VisualizationPropTypes } from '../../common'; import { layerTypes, createBaseMap, LayerTypes } from './layers'; diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts index 6c26d6134..fe4068e20 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts +++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts @@ -25,7 +25,7 @@ export type MapNodeData = { colorAttribute?: string | undefined; colorAttributeType?: string | undefined; colorScale: string; - colorMapping?: { [id: string]: number[] }; + colorMapping?: { [label: string]: [number, number, number] }; }; export type MapEdgeData = { -- GitLab