diff --git a/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx index f16a9a350127f4b81f6cbdd63d827ffecf8a31d5..1ffe9ed536a9b9885833f95dd4de7d99673233ba 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> ); diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/Tooltip.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/Tooltip.tsx index 772e9ea72309441a0448b1ff1fd58a70b9fe5f97..ea5bc92fc41140633a1698955284d7d6a8bc96a1 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/config.ts b/libs/shared/lib/vis/visualizations/mapvis/config.ts new file mode 100644 index 0000000000000000000000000000000000000000..db5f810c65a11bebd484c70566b5229530fdbd8d --- /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 52395513b83a59ebd11cd2ef183643fcf580345b..5641f01820187f0b3a85308fbf483e57b57c02a8 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 9e5f7c31e76c2f3752733dd3c4c0f33ed2462500..92360935c9703cc6d49606b4a90cfe2af9930af2 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 dd407682b8d58002031a3c9d778445accf5ef3f2..a853da41aeda3c90a57fcf4e8737b4eb2efb28c3 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,21 +1,67 @@ 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 { + [label: 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 }) { + // TODO: Remove any here + 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]; @@ -23,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; @@ -31,13 +77,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({ @@ -70,31 +118,50 @@ 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, visible: !layerSettings?.nodes[label]?.hidden, data: nodes, pickable: true, - getFillColor: (d) => layerSettings?.nodes[label]?.color, - getPosition: (d) => getNodeLocation(d._id), - getRadius: (d) => layerSettings?.nodes[label]?.size, + getFillColor: (d) => { + if (layerSettings?.nodes[label].colorByAttribute) { + 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)); + } + } + return layerSettings?.nodes[label].color; + }, + 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: { getIcon: [selected], + getRadius: [layerSettings?.nodes[label].size], + getFillColor: [this.state.colorScales], }, }); 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 5668172943e5401a198876afbbd212c28052b17e..70b08dd096885b67ea95f2299ac23f46bebe83a1 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,15 +3,18 @@ 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'; +import { isEqual } from 'lodash-es'; const defaultNodeSettings = (index: number) => ({ + color: nodeColorRGB(index), + colorMapping: {}, + colorScale: undefined, colorByAttribute: false, colorAttribute: undefined, colorAttributeType: undefined, hidden: false, shape: 'circle', - color: nodeColorHex(index), size: 40, }); @@ -53,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, @@ -161,7 +164,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 +179,7 @@ export function NodeLinkOptions({ /> {nodeSettings.colorAttributeType === 'numerical' ? ( <div> + <p>Select color scale:</p> <DropdownColorLegend value={settings?.colorScale} onChange={(val) => @@ -187,7 +190,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> )} diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx index e48b49d6f9c274564aadccee426e4373115bb0c5..c7e6c50baff8a21a553f572b58eaf860d084cc8b 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx @@ -1,14 +1,17 @@ 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'; 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/mapvis.types.ts b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts index 08ef8bd7a452d4f43447981954e29f729d1a4095..fe4068e203a4dd78d5aaf1e5d14056be85975aef 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?: { [label: string]: [number, number, 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 d45003bd9917c328ef2bbb4493add45c3e0f8c09..9cd704f2abfebb238587147992a41bf14851076e 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/utils.ts +++ b/libs/shared/lib/vis/visualizations/mapvis/utils.ts @@ -1,6 +1,9 @@ import { visualizationColors } from 'config'; +import { NodeType } from '../nodelinkvis/types'; +import { GeoJsonType } from './mapvis.types'; +import { SearchResultType } from './mapvis.types'; -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); @@ -8,3 +11,7 @@ export function nodeColorHex(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; +}; 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 06db43a108157678c730e20e08a56c992ed6fa7f..0000000000000000000000000000000000000000 --- 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; -};