Skip to content

Commit

Permalink
Merge pull request #159 from GNS-Science/feature/dotted_lines
Browse files Browse the repository at this point in the history
Feature/dotted lines
  • Loading branch information
benjamineac authored Apr 4, 2024
2 parents a2a9a2c + 7b88ee2 commit 86f0b50
Show file tree
Hide file tree
Showing 3 changed files with 1,269 additions and 1,002 deletions.
26 changes: 25 additions & 1 deletion src/GroupCurveChart/GroupCurveChart.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import GroupCurveChart from './GroupCurveChart';
import { curveGroup4, curveGroup3, curveGroup2, curveGroup1 } from '../__tests__/testData/uncertaintyTestData';
import spectralAccelUncertaintyTestData from '../__tests__/testData/spectralAccelUncertaintyTestData';
import spectralAccelUncertaintyLog from '../__tests__/testData/spectralAccelUncertaintyLog';
import { multipleImtData } from '../__tests__/testData/hazardChartMultipleSATestData';
import { multipleImtData, uhsData } from '../__tests__/testData/hazardChartMultipleSATestData';

export default {
title: 'Charts/GroupCurveChart',
Expand All @@ -23,6 +23,7 @@ export const SpectralAccelUncertaintyTrue = Template.bind({});
export const SpectralAccelUncertaintyFalse = Template.bind({});
export const SpectralAccelLog = Template.bind({});
export const MultipleSA = Template.bind({});
export const MultipleSAUHS = Template.bind({});

Primary.args = {
scaleType: 'log',
Expand Down Expand Up @@ -224,3 +225,26 @@ MultipleSA.args = {
spectral: false,
timePeriod: 100,
};

MultipleSAUHS.args = {
scaleType: 'log',
yScaleType: 'linear',
xLabel: 'Period [s]',
yLabel: 'Shaking Intensity [g]',
xLimits: [0.0001, 10],
yLimits: [0, 3.690382289232626],
gridColor: '#efefef',
backgroundColor: '#ffffff',
numTickX: 5,
numTickY: 5,
width: 600,
curves: uhsData,
tooltip: true,
crosshair: true,
heading: 'Spectra with Log Xscale',
subHeading: 'WLG 400m/s',
poe: 0.1,
uncertainty: true,
spectral: true,
timePeriod: 100,
};
90 changes: 54 additions & 36 deletions src/GroupCurveChart/GroupCurveChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ import { useTooltip, useTooltipInPortal } from '@visx/tooltip';
import { Line } from '@visx/shape';
import { localPoint } from '@visx/event';
import { bisector } from 'd3-array';
import { Legend, LegendItem, LegendLabel } from '@visx/legend';
import { Legend, LegendItem, LegendLabel, LegendOrdinal } from '@visx/legend';
import { GlyphSquare } from '@visx/glyph';

import { GroupCurveChartProps, Datum } from './groupCurveChart.types';
import { getAreaData, getSortedMeanCurves } from './groupCurveChart.service';
import PlotHeadings from '../common/PlotHeadings';
import { HazardColorScale } from '../types/hazardCharts.types';
import AxisLabel from '../common/AxisLabel';
import { Typography } from '@mui/material';

const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartProps) => {
const {
Expand Down Expand Up @@ -93,6 +92,20 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP

const legendGlyphSize = 15;

const ordinalColorScale = useMemo(() => {
if (!spectral) {
return scaleOrdinal({
domain: !poe ? [...curvesDomain.domain] : [...curvesDomain.domain, !spectral && `POE ${poe * 100}% (${timePeriod} Yrs)`],
range: !poe ? [...curvesDomain.range] : [...curvesDomain.range, !spectral && '#989C9C'],
});
} else {
return scaleOrdinal({
domain: [...curvesDomain.domain],
range: [...curvesDomain.range],
});
}
}, [curvesDomain, poe, spectral, timePeriod]);

const poeLine = useMemo(() => {
const getPoE = (poeValue: number) => {
const yValue = -Math.log(1 - poeValue) / timePeriod;
Expand Down Expand Up @@ -171,8 +184,15 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP

const legendRange = generateLegendRange();

const legendDomain = locationsWithVs30.flat(2);

if (!spectral && poe) {
legendDomain.push(`POE ${poe * 100}% (${timePeriod} Yrs)`);
legendRange.push(<rect key={'poe'} fill={'#989C9C'} width={legendGlyphSize} height={legendGlyphSize / 5} y={7} />);
}

const shapeScale = scaleOrdinal<string, React.FC | React.ReactNode>({
domain: locationsWithVs30.flat(2),
domain: legendDomain,
range: legendRange,
});

Expand Down Expand Up @@ -277,9 +297,8 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP
data={curves[key][curveType].data}
x={(d) => xScale(d[0])}
y={(d) => yScale(d[1])}
stroke={strokeColorArray[index] ?? ''}
stroke={curves[key]['upper1'].strokeColor}
strokeOpacity={curves[key][curveType].strokeOpacity ?? 1}
strokeDasharray={strokeDashArray(index)}
defined={(d, index) => {
if (scaleType === 'log' && index === 0) {
return false;
Expand Down Expand Up @@ -315,13 +334,7 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP
{Object.keys(curves).map((key, index) => (
<Group key={key}>
{scaleType === 'log' && spectral && (
<GlyphSquare
key={key}
size={50}
fill={curves[key]['mean'].strokeColor ?? '#000000'}
left={xScale(curves[key]['mean'].data[0][0])}
top={yScale(curves[key]['mean'].data[0][1])}
/>
<GlyphSquare key={key} size={50} fill={curves[key]['upper1'].strokeColor} left={xScale(curves[key]['mean'].data[0][0])} top={yScale(curves[key]['mean'].data[0][1])} />
)}
<LinePath
key={`${index}-${key}`}
Expand All @@ -330,9 +343,9 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP
x={(d) => xScale(d[0])}
y={(d) => yScale(d[1])}
markerMid="url(#marker-x)"
stroke={strokeColorArray[index] ?? ''}
stroke={spectral ? curves[key]['upper1'].strokeColor : strokeColorArray[index]}
strokeOpacity={curves[key]['mean'].strokeOpacity ?? 1}
strokeDasharray={strokeDashArray(index)}
strokeDasharray={spectral ? '0' : strokeDashArray(index)}
defined={(d, index) => {
if (scaleType === 'log' && index === 0) {
return false;
Expand Down Expand Up @@ -379,28 +392,33 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP
)}
</Group>
</svg>
<div style={{ width: width * 0.24, height: 100, position: 'absolute', top: marginTop, left: width * 0.8, display: 'flex' }}>
<Legend scale={shapeScale}>
{(labels) => (
<div style={{ display: 'flex', flexDirection: 'column' }}>
{labels.map((label, i) => {
// const color = curvesDomain.range[i];
const shape = shapeScale(label.datum);
const isValidElement = React.isValidElement(shape);
return (
<LegendItem key={`legend-quantile-${i}`} margin="0 4px 0 0" flexDirection="row">
<svg width={legendGlyphSize} height={legendGlyphSize} style={{ margin: '4px' }}>
{isValidElement ? React.cloneElement(shape as React.ReactElement) : React.createElement(shape as React.ComponentType<{ fill: string }>)}
</svg>
<LegendLabel align="left" margin={0} size={4}>
<Typography fontSize={'smaller'}>{label.text}</Typography>
</LegendLabel>
</LegendItem>
);
})}
</div>
)}
</Legend>
<div style={{ width: width * 0.24, position: 'absolute', top: marginTop, left: width * 0.8, display: 'flex' }}>
{uncertainty || spectral ? (
<LegendOrdinal direction="column" scale={ordinalColorScale} shape="line" style={{ fontSize: width * 0.016 <= 13 ? 13 : width * 0.015 }} shapeHeight={width * 0.02} />
) : (
<Legend scale={shapeScale}>
{(labels) => (
<div style={{ display: 'flex', flexDirection: 'column' }}>
{labels.map((label, i) => {
const shape = shapeScale(label.datum);
const isValidElement = React.isValidElement(shape);
return (
<LegendItem
key={`legend-quantile-${i}`}
flexDirection="row"
style={{ display: 'flex', alignItems: 'center', fontSize: width * 0.016 <= 13 ? 13 : width * 0.015, flex: '1 1 0%', margin: '0px' }}
>
<svg width={legendGlyphSize} height={legendGlyphSize} style={{ margin: '0px 4px' }}>
{isValidElement ? React.cloneElement(shape as React.ReactElement) : React.createElement(shape as React.ComponentType<{ fill: string }>)}
</svg>
<LegendLabel align="left">{label.text}</LegendLabel>
</LegendItem>
);
})}
</div>
)}
</Legend>
)}
</div>
</div>
</>
Expand Down
Loading

0 comments on commit 86f0b50

Please sign in to comment.