@@ -11,14 +11,15 @@ import { useTooltip, useTooltipInPortal } from '@visx/tooltip';
11
11
import { Line } from '@visx/shape' ;
12
12
import { localPoint } from '@visx/event' ;
13
13
import { bisector } from 'd3-array' ;
14
- import { LegendOrdinal } from '@visx/legend' ;
14
+ import { Legend , LegendItem , LegendLabel } from '@visx/legend' ;
15
15
import { GlyphSquare } from '@visx/glyph' ;
16
16
17
17
import { GroupCurveChartProps , Datum } from './groupCurveChart.types' ;
18
18
import { getAreaData , getSortedMeanCurves } from './groupCurveChart.service' ;
19
19
import PlotHeadings from '../common/PlotHeadings' ;
20
20
import { HazardColorScale } from '../types/hazardCharts.types' ;
21
21
import AxisLabel from '../common/AxisLabel' ;
22
+ import { Typography } from '@mui/material' ;
22
23
23
24
const GroupCurveChart : React . FC < GroupCurveChartProps > = ( props : GroupCurveChartProps ) => {
24
25
const {
@@ -90,19 +91,7 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP
90
91
return colorScale ;
91
92
} , [ curves ] ) ;
92
93
93
- const ordinalColorScale = useMemo ( ( ) => {
94
- if ( ! spectral ) {
95
- return scaleOrdinal ( {
96
- domain : ! poe ? [ ...curvesDomain . domain ] : [ ...curvesDomain . domain , ! spectral && `POE ${ poe * 100 } % (${ timePeriod } Yrs)` ] ,
97
- range : ! poe ? [ ...curvesDomain . range ] : [ ...curvesDomain . range , ! spectral && '#989C9C' ] ,
98
- } ) ;
99
- } else {
100
- return scaleOrdinal ( {
101
- domain : [ ...curvesDomain . domain ] ,
102
- range : [ ...curvesDomain . range ] ,
103
- } ) ;
104
- }
105
- } , [ curvesDomain , poe , spectral , timePeriod ] ) ;
94
+ const legendGlyphSize = 15 ;
106
95
107
96
const poeLine = useMemo ( ( ) => {
108
97
const getPoE = ( poeValue : number ) => {
@@ -120,6 +109,73 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP
120
109
detectBounds : true ,
121
110
} ) ;
122
111
112
+ const locations = [ ...new Set ( Object . keys ( curves ) . map ( ( k ) => k . split ( ' ' ) [ 2 ] ) ) ] ;
113
+ const vs30s = [ ...new Set ( Object . keys ( curves ) . map ( ( k ) => k . split ( ' ' ) [ 0 ] ) ) ] ;
114
+ const imts = [ ...new Set ( Object . keys ( curves ) . map ( ( k ) => k . split ( ' ' ) [ 1 ] ) ) ] ;
115
+
116
+ const locationsWithVs30 = locations . map ( ( location ) => {
117
+ return vs30s . map ( ( vs30 ) => {
118
+ return curvesDomain . domain . filter ( ( k ) => ~ k . indexOf ( vs30 ) && ~ k . indexOf ( location ) ) ;
119
+ } ) ;
120
+ } ) ;
121
+
122
+ const generateLegendRange = ( ) => {
123
+ const domain = locationsWithVs30 . flat ( 1 ) ;
124
+
125
+ return domain
126
+ . map ( ( d , i ) => {
127
+ switch ( d . length ) {
128
+ case 1 :
129
+ return [ < rect key = { 'a' + i } fill = { curvesDomain . range [ i ] } width = { legendGlyphSize } height = { legendGlyphSize / 5 } y = { 7 } /> ] ;
130
+ case 2 :
131
+ return [
132
+ < rect key = { 'a' + i } fill = { curvesDomain . range [ i ] } width = { legendGlyphSize } height = { legendGlyphSize / 5 } y = { 7 } /> ,
133
+ < line key = { 'b' + i } stroke = { curvesDomain . range [ i ] } x1 = "0" y1 = "10" x2 = "250" y2 = "10" strokeDasharray = "4,5" y = { 7 } strokeWidth = { legendGlyphSize / 5 } /> ,
134
+ ] ;
135
+ case 3 :
136
+ return [
137
+ < rect key = { 'a' + i } fill = { curvesDomain . range [ i ] } width = { legendGlyphSize } height = { legendGlyphSize / 5 } y = { 7 } /> ,
138
+ < line key = { 'b' + i } stroke = { curvesDomain . range [ i ] } x1 = "0" y1 = "10" x2 = "250" y2 = "10" strokeDasharray = "4,5" y = { 7 } strokeWidth = { legendGlyphSize / 5 } /> ,
139
+ < line key = { 'c' + i } stroke = { curvesDomain . range [ i ] } x1 = "0" y1 = "10" x2 = "250" y2 = "10" strokeDasharray = "1,3" y = { 7 } strokeWidth = { legendGlyphSize / 5 } /> ,
140
+ ] ;
141
+ default :
142
+ return [ ] ;
143
+ }
144
+ } )
145
+ . flat ( ) ;
146
+ } ;
147
+
148
+ const strokeDashArray = ( index : number ) => {
149
+ const values = [ '0' , '4,5' , '1,3' ] ;
150
+ const repeatCount = imts . length === 1 ? 1 : imts . length === 2 ? 2 : imts . length === 3 ? 3 : 1 ;
151
+ const valuesIndex = ( index % repeatCount ) % values . length ;
152
+
153
+ return values [ valuesIndex ] ;
154
+ } ;
155
+
156
+ function generateColorArray ( length : number , interval : number ) {
157
+ const array : string [ ] = [ ] ;
158
+ let valuesIndex = 0 ;
159
+ for ( let i = 0 ; i < length ; i ++ ) {
160
+ if ( i % interval === 0 ) {
161
+ array . push ( curvesDomain . range [ valuesIndex ] ) ;
162
+ valuesIndex = ( valuesIndex + 1 ) % curvesDomain . range . length ;
163
+ } else {
164
+ array . push ( array [ i - 1 ] ) ;
165
+ }
166
+ }
167
+ return array ;
168
+ }
169
+
170
+ const strokeColorArray = generateColorArray ( Object . keys ( curves ) . length , imts . length ) ;
171
+
172
+ const legendRange = generateLegendRange ( ) ;
173
+
174
+ const shapeScale = scaleOrdinal < string , React . FC | React . ReactNode > ( {
175
+ domain : locationsWithVs30 . flat ( 2 ) ,
176
+ range : legendRange ,
177
+ } ) ;
178
+
123
179
const {
124
180
showTooltip,
125
181
tooltipOpen,
@@ -221,8 +277,9 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP
221
277
data = { curves [ key ] [ curveType ] . data }
222
278
x = { ( d ) => xScale ( d [ 0 ] ) }
223
279
y = { ( d ) => yScale ( d [ 1 ] ) }
224
- stroke = { curves [ key ] [ curveType ] . strokeColor ?? '' }
280
+ stroke = { strokeColorArray [ index ] ?? '' }
225
281
strokeOpacity = { curves [ key ] [ curveType ] . strokeOpacity ?? 1 }
282
+ strokeDasharray = { strokeDashArray ( index ) }
226
283
defined = { ( d , index ) => {
227
284
if ( scaleType === 'log' && index === 0 ) {
228
285
return false ;
@@ -240,7 +297,7 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP
240
297
clipAboveTo = { 0 }
241
298
clipBelowTo = { yMax }
242
299
aboveAreaProps = { {
243
- fill : curves [ key ] [ 'upper1' ] . strokeColor ,
300
+ fill : strokeColorArray [ index ] ,
244
301
fillOpacity : 0.4 ,
245
302
} }
246
303
defined = { ( d , index ) => {
@@ -272,8 +329,10 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP
272
329
data = { curves [ key ] [ 'mean' ] . data }
273
330
x = { ( d ) => xScale ( d [ 0 ] ) }
274
331
y = { ( d ) => yScale ( d [ 1 ] ) }
275
- stroke = { curves [ key ] [ 'mean' ] . strokeColor ?? '' }
332
+ markerMid = "url(#marker-x)"
333
+ stroke = { strokeColorArray [ index ] ?? '' }
276
334
strokeOpacity = { curves [ key ] [ 'mean' ] . strokeOpacity ?? 1 }
335
+ strokeDasharray = { strokeDashArray ( index ) }
277
336
defined = { ( d , index ) => {
278
337
if ( scaleType === 'log' && index === 0 ) {
279
338
return false ;
@@ -320,8 +379,28 @@ const GroupCurveChart: React.FC<GroupCurveChartProps> = (props: GroupCurveChartP
320
379
) }
321
380
</ Group >
322
381
</ svg >
323
- < div style = { { width : width * 0.24 , height : 100 , position : 'absolute' , top : marginTop , left : width * 0.7 , display : 'flex' } } >
324
- < LegendOrdinal direction = "column" scale = { ordinalColorScale } shape = "line" style = { { fontSize : width * 0.016 <= 13 ? 13 : width * 0.015 } } shapeHeight = { width * 0.02 } />
382
+ < div style = { { width : width * 0.24 , height : 100 , position : 'absolute' , top : marginTop , left : width * 0.8 , display : 'flex' } } >
383
+ < Legend scale = { shapeScale } >
384
+ { ( labels ) => (
385
+ < div style = { { display : 'flex' , flexDirection : 'column' } } >
386
+ { labels . map ( ( label , i ) => {
387
+ // const color = curvesDomain.range[i];
388
+ const shape = shapeScale ( label . datum ) ;
389
+ const isValidElement = React . isValidElement ( shape ) ;
390
+ return (
391
+ < LegendItem key = { `legend-quantile-${ i } ` } margin = "0 4px 0 0" flexDirection = "row" >
392
+ < svg width = { legendGlyphSize } height = { legendGlyphSize } style = { { margin : '4px' } } >
393
+ { isValidElement ? React . cloneElement ( shape as React . ReactElement ) : React . createElement ( shape as React . ComponentType < { fill : string } > ) }
394
+ </ svg >
395
+ < LegendLabel align = "left" margin = { 0 } size = { 4 } >
396
+ < Typography fontSize = { 'smaller' } > { label . text } </ Typography >
397
+ </ LegendLabel >
398
+ </ LegendItem >
399
+ ) ;
400
+ } ) }
401
+ </ div >
402
+ ) }
403
+ </ Legend >
325
404
</ div >
326
405
</ div >
327
406
</ >
0 commit comments