Skip to content

Commit ec04b93

Browse files
add log scale for scatter plot
1 parent 68152a7 commit ec04b93

File tree

8 files changed

+142
-22
lines changed

8 files changed

+142
-22
lines changed

src/Components/AggregatedDataExplorer.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ function AggregatedDataExplorer(props: Props) {
6060
signatureSolution: undefined,
6161
signatureSolutionForDataList: 'All',
6262
keepAxisSame: false,
63+
xScaleType: 'linear',
64+
yScaleType: 'linear',
6365
};
6466

6567
const [state, dispatch] = useReducer(Reducer, initialState);
@@ -202,18 +204,35 @@ function AggregatedDataExplorer(props: Props) {
202204
payload: useSameRange,
203205
});
204206
};
207+
205208
const updateBarLayout = (verticalBarLayout: boolean) => {
206209
dispatch({
207210
type: 'UPDATE_BAR_LAYOUT',
208211
payload: verticalBarLayout,
209212
});
210213
};
214+
211215
const updateKeepAxisSame = (d: boolean) => {
212216
dispatch({
213217
type: 'UPDATE_KEEP_AXIS_SAME',
214218
payload: d,
215219
});
216220
};
221+
222+
const updateXScaleType = (d: 'linear' | 'log') => {
223+
dispatch({
224+
type: 'UPDATE_X_SCALE_TYPE',
225+
payload: d,
226+
});
227+
};
228+
229+
const updateYScaleType = (d: 'linear' | 'log') => {
230+
dispatch({
231+
type: 'UPDATE_Y_SCALE_TYPE',
232+
payload: d,
233+
});
234+
};
235+
217236
return (
218237
<div>
219238
<div
@@ -287,6 +306,8 @@ function AggregatedDataExplorer(props: Props) {
287306
updateBarLayout,
288307
updateSignatureSolutionForDataList,
289308
updateKeepAxisSame,
309+
updateXScaleType,
310+
updateYScaleType,
290311
}}
291312
>
292313
<div

src/Components/CountryVisualization.tsx

+18
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ function CountryVisualization(props: Props) {
9494
disaggregationGraphType: 'country',
9595
disaggregationOrder: 'first',
9696
keepAxisSame: false,
97+
xScaleType: 'linear',
98+
yScaleType: 'linear',
9799
};
98100

99101
const [state, dispatch] = useReducer(Reducer, initialState);
@@ -278,6 +280,20 @@ function CountryVisualization(props: Props) {
278280
payload: d,
279281
});
280282
};
283+
284+
const updateXScaleType = (d: 'linear' | 'log') => {
285+
dispatch({
286+
type: 'UPDATE_X_SCALE_TYPE',
287+
payload: d,
288+
});
289+
};
290+
291+
const updateYScaleType = (d: 'linear' | 'log') => {
292+
dispatch({
293+
type: 'UPDATE_Y_SCALE_TYPE',
294+
payload: d,
295+
});
296+
};
281297
return (
282298
<Context.Provider
283299
// eslint-disable-next-line react/jsx-no-constructed-context-values
@@ -307,6 +323,8 @@ function CountryVisualization(props: Props) {
307323
updateDisaggregationGraphType,
308324
updateDisaggregationOrder,
309325
updateKeepAxisSame,
326+
updateXScaleType,
327+
updateYScaleType,
310328
}}
311329
>
312330
<div className='undp-container'>

src/Context/Context.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ const Context = createContext<CtxDataType>({
3434
signatureSolutionForDataList: 'All',
3535
showReference: false,
3636
keepAxisSame: false,
37+
xScaleType: 'linear',
38+
yScaleType: 'linear',
3739
updateGraphType: (
3840
_d:
3941
| 'scatterPlot'
@@ -76,6 +78,8 @@ const Context = createContext<CtxDataType>({
7678
| 'Resilience',
7779
) => {},
7880
updateKeepAxisSame: (_d: boolean) => {},
81+
updateXScaleType: (_d: 'linear' | 'log') => {},
82+
updateYScaleType: (_d: 'linear' | 'log') => {},
7983
});
8084

8185
export default Context;

src/Context/Reducer.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export default (state: any, action: any) => {
4848
return { ...state, disaggregationOrder: action.payload };
4949
case 'UPDATE_KEEP_AXIS_SAME':
5050
return { ...state, keepAxisSame: action.payload };
51+
case 'UPDATE_X_SCALE_TYPE':
52+
return { ...state, xScaleType: action.payload };
53+
case 'UPDATE_Y_SCALE_TYPE':
54+
return { ...state, yScaleType: action.payload };
5155
default:
5256
return { ...state };
5357
}

src/GrapherComponent/ScatterPlot/Graph.tsx

+49-22
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import maxBy from 'lodash.maxby';
44
import max from 'lodash.max';
55
import orderBy from 'lodash.orderby';
66
import { Delaunay } from 'd3-delaunay';
7-
import { scaleOrdinal, scaleLinear, scaleThreshold, scaleSqrt } from 'd3-scale';
7+
import {
8+
scaleOrdinal,
9+
scaleLinear,
10+
scaleThreshold,
11+
scaleSqrt,
12+
scaleLog,
13+
} from 'd3-scale';
814
import minBy from 'lodash.minby';
915
import UNDPColorModule from 'undp-viz-colors';
1016
import flattenDeep from 'lodash.flattendeep';
@@ -49,6 +55,8 @@ export function Graph(props: Props) {
4955
selectedIncomeGroups,
5056
selectedCountryGroup,
5157
keepAxisSame,
58+
xScaleType,
59+
yScaleType,
5260
} = useContext(Context) as CtxDataType;
5361
const [selectedColor, setSelectedColor] = useState<string | undefined>(
5462
undefined,
@@ -342,17 +350,28 @@ export function Graph(props: Props) {
342350
: refYVal
343351
: (minBy(dataFormatted, d => d.yVal)?.yVal as number)
344352
: 0;
345-
346-
const xScale = scaleLinear()
347-
.domain([xMinValue > 0 ? 0 : xMinValue, xMaxValue])
348-
.range([0, graphWidth])
349-
.nice();
350-
const yScale = scaleLinear()
351-
.domain([yMinValue > 0 ? 0 : yMinValue, yMaxValue])
352-
.range([graphHeight, 0])
353-
.nice();
354-
const xTicks = xScale.ticks(5);
355-
const yTicks = yScale.ticks(5);
353+
const xScaleLogAllowed = !(
354+
xScaleType === 'linear' ||
355+
fullArray.filter(d => (d.x as number) <= 0).length > 0
356+
);
357+
const yScaleLogAllowed = !(
358+
yScaleType === 'linear' ||
359+
fullArray.filter(d => (d.y as number) <= 0).length > 0
360+
);
361+
const xScale = !xScaleLogAllowed
362+
? scaleLinear()
363+
.domain([xMinValue > 0 ? 0 : xMinValue, xMaxValue])
364+
.range([0, graphWidth])
365+
.nice()
366+
: scaleLog().domain([xMinValue, xMaxValue]).range([0, graphWidth]).nice();
367+
const yScale = !yScaleLogAllowed
368+
? scaleLinear()
369+
.domain([yMinValue > 0 ? 0 : yMinValue, yMaxValue])
370+
.range([graphHeight, 0])
371+
.nice()
372+
: scaleLog().domain([yMinValue, yMaxValue]).range([graphHeight, 0]).nice();
373+
const xTicks = !xScaleLogAllowed ? xScale.ticks(5) : xScale.ticks(3);
374+
const yTicks = !yScaleLogAllowed ? yScale.ticks(5) : yScale.ticks(3);
356375
const voronoiDiagram = Delaunay.from(
357376
dataFormatted,
358377
d => xScale(d.xVal as number),
@@ -552,6 +571,7 @@ export function Graph(props: Props) {
552571
stroke='#AAA'
553572
strokeWidth={1}
554573
strokeDasharray='4,8'
574+
opacity={yScaleLogAllowed && i === 0 ? 0 : 1}
555575
/>
556576
<text
557577
x={0}
@@ -569,21 +589,21 @@ export function Graph(props: Props) {
569589
<line
570590
x1={0}
571591
x2={graphWidth}
572-
y1={yScale(0)}
573-
y2={yScale(0)}
592+
y1={yScaleLogAllowed ? graphHeight : yScale(0)}
593+
y2={yScaleLogAllowed ? graphHeight : yScale(0)}
574594
stroke={UNDPColorModule.graphGray}
575595
strokeWidth={1}
576596
/>
577597
<text
578598
x={0}
579-
y={yScale(0)}
599+
y={yScaleLogAllowed ? graphHeight : yScale(0)}
580600
fill={UNDPColorModule.graphGray}
581601
textAnchor='end'
582602
fontSize={12}
583603
dy={4}
584604
dx={-3}
585605
>
586-
0
606+
{yScaleLogAllowed ? '' : 0}
587607
</text>
588608
<text
589609
transform={`translate(-50, ${graphHeight / 2}) rotate(-90)`}
@@ -602,6 +622,7 @@ export function Graph(props: Props) {
602622
TRUNCATE_MAX_TEXT_LENGTH,
603623
)}...`
604624
: yIndicatorMetaData.IndicatorLabel}
625+
{yScaleLogAllowed ? ' (Log scale)' : ''}
605626
</text>
606627
</g>
607628
<g>
@@ -615,14 +636,15 @@ export function Graph(props: Props) {
615636
stroke='#AAA'
616637
strokeWidth={1}
617638
strokeDasharray='4,8'
639+
opacity={xScaleLogAllowed && i === 0 ? 0 : 1}
618640
/>
619641
<text
620642
x={xScale(d)}
621643
y={graphHeight}
622644
fill={UNDPColorModule.graphGray}
623645
textAnchor='middle'
624646
fontSize={12}
625-
dy={12}
647+
dy={15}
626648
>
627649
{Math.abs(d) < 1 ? d : format('~s')(d).replace('G', 'B')}
628650
</text>
@@ -631,20 +653,20 @@ export function Graph(props: Props) {
631653
<line
632654
y1={0}
633655
y2={graphHeight}
634-
x1={xScale(0)}
635-
x2={xScale(0)}
656+
x1={xScaleLogAllowed ? 0 : xScale(0)}
657+
x2={xScaleLogAllowed ? 0 : xScale(0)}
636658
stroke={UNDPColorModule.graphGray}
637659
strokeWidth={1}
638660
/>
639661
<text
640-
x={xScale(0)}
662+
x={xScaleLogAllowed ? 0 : xScale(0)}
641663
y={graphHeight}
642664
fill={UNDPColorModule.graphGray}
643665
textAnchor='middle'
644666
fontSize={12}
645667
dy={15}
646668
>
647-
{0}
669+
{xScaleLogAllowed ? '' : 0}
648670
</text>
649671
<text
650672
transform={`translate(${graphWidth / 2}, ${graphHeight})`}
@@ -664,6 +686,7 @@ export function Graph(props: Props) {
664686
TRUNCATE_MAX_TEXT_LENGTH,
665687
)}...`
666688
: xIndicatorMetaData.IndicatorLabel}
689+
{xScaleLogAllowed ? ' (Log scale)' : ''}
667690
</text>
668691
</g>
669692

@@ -866,7 +889,11 @@ export function Graph(props: Props) {
866889
: UNDPColorModule.graphGray
867890
}
868891
/>
869-
{showLabel ? (
892+
{showLabel &&
893+
(selectedCountries.length === 0 ||
894+
selectedCountries.indexOf(
895+
countryData['Country or Area'],
896+
) !== -1) ? (
870897
<text
871898
fontSize={10}
872899
fill={

src/GrapherComponent/Settings/ScatterPlotSettings.tsx

+24
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export function ScatterPlotSettings(props: Props) {
4141
showMostRecentData,
4242
selectedCountryOrRegion,
4343
showReference,
44+
xScaleType,
45+
yScaleType,
4446
updateColorIndicator,
4547
updateXAxisIndicator,
4648
updateYAxisIndicator,
@@ -49,6 +51,8 @@ export function ScatterPlotSettings(props: Props) {
4951
updateShowMostRecentData,
5052
updateShowReference,
5153
updateKeepAxisSame,
54+
updateXScaleType,
55+
updateYScaleType,
5256
} = useContext(Context) as CtxDataType;
5357
const scatterPlotIndicators = indicators.filter(d => !d.IsCategorical);
5458
const sizeIndicators = indicators.filter(d => d.Sizing);
@@ -252,6 +256,26 @@ export function ScatterPlotSettings(props: Props) {
252256
>
253257
Use same axes to compare between years
254258
</Checkbox>
259+
<Checkbox
260+
style={{ margin: 0 }}
261+
className='undp-checkbox'
262+
checked={xScaleType === 'log'}
263+
onChange={e => {
264+
updateXScaleType(e.target.checked ? 'log' : 'linear');
265+
}}
266+
>
267+
Use log scale for x-axis
268+
</Checkbox>
269+
<Checkbox
270+
style={{ margin: 0 }}
271+
className='undp-checkbox'
272+
checked={yScaleType === 'log'}
273+
onChange={e => {
274+
updateYScaleType(e.target.checked ? 'log' : 'linear');
275+
}}
276+
>
277+
Use log scale for y-axis
278+
</Checkbox>
255279
</div>
256280
</div>
257281
{!selectedCountryOrRegion && countries.length > 1 ? (

src/RegionVisualization/Visualization.tsx

+18
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ function VisualizationEl(props: Props) {
9292
disaggregationGraphType: 'global',
9393
disaggregationOrder: 'first',
9494
keepAxisSame: false,
95+
xScaleType: 'linear',
96+
yScaleType: 'linear',
9597
};
9698

9799
const [state, dispatch] = useReducer(Reducer, initialState);
@@ -277,6 +279,20 @@ function VisualizationEl(props: Props) {
277279
payload: d,
278280
});
279281
};
282+
283+
const updateXScaleType = (d: 'linear' | 'log') => {
284+
dispatch({
285+
type: 'UPDATE_X_SCALE_TYPE',
286+
payload: d,
287+
});
288+
};
289+
290+
const updateYScaleType = (d: 'linear' | 'log') => {
291+
dispatch({
292+
type: 'UPDATE_Y_SCALE_TYPE',
293+
payload: d,
294+
});
295+
};
280296
return (
281297
<Context.Provider
282298
// eslint-disable-next-line react/jsx-no-constructed-context-values
@@ -306,6 +322,8 @@ function VisualizationEl(props: Props) {
306322
updateDisaggregationGraphType,
307323
updateDisaggregationOrder,
308324
updateKeepAxisSame,
325+
updateXScaleType,
326+
updateYScaleType,
309327
}}
310328
>
311329
<div>

src/Types.ts

+4
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ export interface CtxDataType {
169169
| 'Poverty and Inequality'
170170
| 'Resilience';
171171
keepAxisSame: boolean;
172+
xScaleType: 'linear' | 'log';
173+
yScaleType: 'linear' | 'log';
172174
updateGraphType: (
173175
_d:
174176
| 'scatterPlot'
@@ -211,6 +213,8 @@ export interface CtxDataType {
211213
| 'Resilience',
212214
) => void;
213215
updateKeepAxisSame: (_d: boolean) => void;
216+
updateXScaleType: (_d: 'linear' | 'log') => void;
217+
updateYScaleType: (_d: 'linear' | 'log') => void;
214218
}
215219

216220
export interface CountryListType {

0 commit comments

Comments
 (0)