Skip to content

Commit 938ca57

Browse files
authored
Merge pull request #919 from visualize-admin/feat/use-abbreviations
feat: Use abbreviations
2 parents 57ef877 + f284d00 commit 938ca57

33 files changed

+949
-620
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ You can also check the [release page](https://github.com/visualize-admin/visuali
1010
## Unreleased
1111

1212
- Metadata is now shown in a dedicated panel that can be reached both from editor & published mode
13+
- It's now possible to use abbreviations in color segmentations and X fields for dimension values with schema:alternateName properties
1314

1415
## [3.15.0] - 2022-11-29
1516

app/charts/area/areas-state.tsx

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ import {
1717
stackOrderReverse,
1818
sum,
1919
} from "d3";
20-
import keyBy from "lodash/keyBy";
2120
import orderBy from "lodash/orderBy";
22-
import { ReactNode, useCallback, useMemo } from "react";
21+
import { ReactNode, useMemo } from "react";
2322

2423
import { LEFT_MARGIN_OFFSET } from "@/charts/area/constants";
2524
import { BRUSH_BOTTOM_SPACE } from "@/charts/shared/brush";
@@ -30,18 +29,22 @@ import {
3029
useOptionalNumericVariable,
3130
usePlottableData,
3231
useDataAfterInteractiveFilters,
33-
useSegment,
3432
useStringVariable,
3533
useTemporalVariable,
3634
} from "@/charts/shared/chart-helpers";
3735
import { CommonChartState } from "@/charts/shared/chart-state";
3836
import { TooltipInfo } from "@/charts/shared/interaction/tooltip";
37+
import { useMaybeAbbreviations } from "@/charts/shared/use-abbreviations";
3938
import useChartFormatters from "@/charts/shared/use-chart-formatters";
4039
import { ChartContext, ChartProps } from "@/charts/shared/use-chart-state";
4140
import { InteractionProvider } from "@/charts/shared/use-interaction";
4241
import { Observer, useWidth } from "@/charts/shared/use-width";
4342
import { AreaFields } from "@/configurator";
44-
import { isTemporalDimension, Observation } from "@/domain/data";
43+
import {
44+
DimensionValue,
45+
isTemporalDimension,
46+
Observation,
47+
} from "@/domain/data";
4548
import {
4649
useFormatNumber,
4750
formatNumberWithUnit,
@@ -112,25 +115,26 @@ const useAreasState = (
112115
const getX = useTemporalVariable(fields.x.componentIri);
113116
const getY = useOptionalNumericVariable(fields.y.componentIri);
114117
const getGroups = useStringVariable(fields.x.componentIri);
115-
const getSegment = useSegment(fields.segment?.componentIri);
116118

117-
const { segmentValuesByLabel, segmentValuesByValue } = useMemo(() => {
118-
const segmentDimension = dimensions.find(
119-
(d) => d.iri === fields.segment?.componentIri
120-
) || { values: [] };
121-
return {
122-
segmentValuesByValue: keyBy(segmentDimension.values, (x) => x.value),
123-
segmentValuesByLabel: keyBy(segmentDimension.values, (x) => x.label),
124-
};
125-
}, [dimensions, fields.segment?.componentIri]);
126-
127-
const getSegmentLabel = useCallback(
128-
(segment: string): string => {
129-
return segmentValuesByValue[segment]?.label || segment;
130-
},
131-
[segmentValuesByValue]
119+
const segmentDimension = dimensions.find(
120+
(d) => d.iri === fields.segment?.componentIri
132121
);
133122

123+
const {
124+
getAbbreviationOrLabelByValue: getSegment,
125+
getLabelByAbbreviation: getSegmentLabel,
126+
abbreviationOrLabelLookup: segmentsByAbbreviationOrLabel,
127+
} = useMaybeAbbreviations({
128+
useAbbreviations: fields.segment?.useAbbreviations ?? false,
129+
dimension: segmentDimension,
130+
});
131+
132+
const segmentsByValue = useMemo(() => {
133+
const values = (segmentDimension?.values || []) as DimensionValue[];
134+
135+
return new Map(values.map((d) => [d.value, d]));
136+
}, [segmentDimension?.values]);
137+
134138
const hasSegment = fields.segment;
135139
const allSegments = useMemo(
136140
() => [...new Set(data.map((d) => getSegment(d)))],
@@ -221,25 +225,24 @@ const useAreasState = (
221225
const uniqueSegments = Array.from(
222226
new Set(plottableSortedData.map((d) => getSegment(d)))
223227
);
224-
const dimension = dimensions.find(
225-
(dim) => dim.iri === fields.segment?.componentIri
226-
);
227-
const sorters = makeDimensionValueSorters(dimension, {
228+
const sorters = makeDimensionValueSorters(segmentDimension, {
228229
sorting: segmentSorting,
229230
sumsBySegment: totalValueBySegment,
231+
useAbbreviations: fields.segment?.useAbbreviations,
230232
});
233+
231234
return orderBy(
232235
uniqueSegments,
233236
sorters,
234237
getSortingOrders(sorters, segmentSorting)
235238
);
236239
}, [
237240
plottableSortedData,
238-
dimensions,
241+
segmentDimension,
239242
segmentSorting,
240243
getY,
241244
getSegment,
242-
fields.segment?.componentIri,
245+
fields.segment?.useAbbreviations,
243246
]);
244247

245248
/** Transform data */
@@ -285,8 +288,9 @@ const useAreasState = (
285288
if (fields.segment && segmentDimension && fields.segment.colorMapping) {
286289
const orderedSegmentLabelsAndColors = segments.map((segment) => {
287290
const dvIri =
288-
segmentValuesByLabel[segment]?.value ||
289-
segmentValuesByValue[segment]?.value;
291+
segmentsByAbbreviationOrLabel.get(segment)?.value ||
292+
segmentsByValue.get(segment)?.value ||
293+
"";
290294

291295
return {
292296
label: segment,
@@ -309,8 +313,8 @@ const useAreasState = (
309313
getX,
310314
plottableSortedData,
311315
preparedData,
312-
segmentValuesByLabel,
313-
segmentValuesByValue,
316+
segmentsByAbbreviationOrLabel,
317+
segmentsByValue,
314318
segments,
315319
series,
316320
]);

app/charts/chart-config-ui-options.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ export type EncodingOption =
2222
type: "component";
2323
optional: boolean;
2424
componentTypes: ComponentType[];
25+
enableUseAbbreviations: boolean;
2526
}
2627
| { field: "imputationType" }
2728
| { field: "showStandardError" }
2829
| { field: "sorting" }
29-
| { field: "size"; componentTypes: ComponentType[]; optional: boolean };
30+
| { field: "size"; componentTypes: ComponentType[]; optional: boolean }
31+
| { field: "useAbbreviations" };
3032

3133
/**
3234
* @todo
@@ -120,6 +122,7 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
120122
options: [
121123
{ field: "color", type: "palette" },
122124
{ field: "imputationType" },
125+
{ field: "useAbbreviations" },
123126
],
124127
},
125128
],
@@ -151,6 +154,7 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
151154
{ sortingType: "byMeasure", sortingOrder: ["asc", "desc"] },
152155
{ sortingType: "byDimensionLabel", sortingOrder: ["asc", "desc"] },
153156
],
157+
options: [{ field: "useAbbreviations" }],
154158
},
155159
{
156160
field: "segment",
@@ -161,6 +165,7 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
161165
options: [
162166
{ field: "chartSubType" },
163167
{ field: "color", type: "palette" },
168+
{ field: "useAbbreviations" },
164169
],
165170
},
166171
],
@@ -187,7 +192,10 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
187192
componentTypes: SEGMENT_COMPONENT_TYPES,
188193
filters: true,
189194
sorting: LINE_SEGMENT_SORTING,
190-
options: [{ field: "color", type: "palette" }],
195+
options: [
196+
{ field: "color", type: "palette" },
197+
{ field: "useAbbreviations" },
198+
],
191199
},
192200
],
193201
interactiveFilters: ["legend", "timeRange"],
@@ -214,6 +222,7 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
214222
type: "component",
215223
componentTypes: ["NumericalMeasure", "OrdinalMeasure"],
216224
optional: false,
225+
enableUseAbbreviations: true,
217226
},
218227
],
219228
},
@@ -241,6 +250,7 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
241250
"OrdinalDimension",
242251
],
243252
optional: true,
253+
enableUseAbbreviations: true,
244254
},
245255
],
246256
},
@@ -262,7 +272,10 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
262272
componentTypes: SEGMENT_COMPONENT_TYPES,
263273
filters: true,
264274
sorting: PIE_SEGMENT_SORTING,
265-
options: [{ field: "color", type: "palette" }],
275+
options: [
276+
{ field: "color", type: "palette" },
277+
{ field: "useAbbreviations" },
278+
],
266279
},
267280
],
268281
interactiveFilters: ["legend"],
@@ -287,12 +300,16 @@ export const chartConfigOptionsUISpec: ChartSpecs = {
287300
optional: true,
288301
componentTypes: SEGMENT_COMPONENT_TYPES,
289302
filters: true,
290-
options: [{ field: "color", type: "palette" }],
303+
options: [
304+
{ field: "color", type: "palette" },
305+
{ field: "useAbbreviations" },
306+
],
291307
},
292308
],
293309
interactiveFilters: ["legend"],
294310
},
295311
table: {
312+
// TODO: Add abbreviations here.
296313
chartType: "table",
297314
encodings: [],
298315
interactiveFilters: [],

app/charts/column/columns-grouped-state.tsx

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ import {
1616
sum,
1717
} from "d3";
1818
import get from "lodash/get";
19-
import keyBy from "lodash/keyBy";
2019
import orderBy from "lodash/orderBy";
21-
import React, { ReactNode, useCallback, useMemo } from "react";
20+
import React, { ReactNode, useMemo } from "react";
2221

2322
import {
2423
BOTTOM_MARGIN_OFFSET,
@@ -32,13 +31,12 @@ import {
3231
useDataAfterInteractiveFilters,
3332
useOptionalNumericVariable,
3433
usePlottableData,
35-
useSegment,
36-
useStringVariable,
3734
useTemporalVariable,
3835
} from "@/charts/shared/chart-helpers";
3936
import { CommonChartState } from "@/charts/shared/chart-state";
4037
import { TooltipInfo } from "@/charts/shared/interaction/tooltip";
4138
import { useChartPadding } from "@/charts/shared/padding";
39+
import { useMaybeAbbreviations } from "@/charts/shared/use-abbreviations";
4240
import useChartFormatters from "@/charts/shared/use-chart-formatters";
4341
import { ChartContext, ChartProps } from "@/charts/shared/use-chart-state";
4442
import { InteractionProvider } from "@/charts/shared/use-interaction";
@@ -49,7 +47,11 @@ import {
4947
useErrorMeasure,
5048
useErrorRange,
5149
} from "@/configurator/components/ui-helpers";
52-
import { isTemporalDimension, Observation } from "@/domain/data";
50+
import {
51+
DimensionValue,
52+
isTemporalDimension,
53+
Observation,
54+
} from "@/domain/data";
5355
import { useFormatNumber, formatNumberWithUnit } from "@/formatters";
5456
import { DimensionMetadataFragment } from "@/graphql/query-hooks";
5557
import { getPalette } from "@/palettes";
@@ -112,33 +114,37 @@ const useGroupedColumnsState = (
112114

113115
const xIsTime = isTemporalDimension(xDimension);
114116

115-
const getX = useStringVariable(fields.x.componentIri);
117+
const { getAbbreviationOrLabelByValue: getX } = useMaybeAbbreviations({
118+
useAbbreviations: fields.x.useAbbreviations ?? false,
119+
dimension: xDimension,
120+
});
121+
116122
const getXAsDate = useTemporalVariable(fields.x.componentIri);
117123
const getY = useOptionalNumericVariable(fields.y.componentIri);
118124
const errorMeasure = useErrorMeasure(chartProps, fields.y.componentIri);
119125
const getYErrorRange = useErrorRange(errorMeasure, getY);
120-
const getSegment = useSegment(fields.segment?.componentIri);
121-
122-
const showStandardError = get(fields, ["y", "showStandardError"], true);
123126

124-
const { segmentValuesByValue } = useMemo(() => {
125-
const segmentDimension = dimensions.find(
126-
(d) => d.iri === fields.segment?.componentIri
127-
) || { values: [] };
128-
return {
129-
segmentValuesByValue: keyBy(segmentDimension.values, (x) => x.value),
130-
segmentValuesByLabel: keyBy(segmentDimension.values, (x) => x.label),
131-
};
132-
}, [dimensions, fields.segment?.componentIri]);
133-
134-
/** When segment values are IRIs, we need to find show the label */
135-
const getSegmentLabel = useCallback(
136-
(segment: string): string => {
137-
return segmentValuesByValue[segment]?.label || segment;
138-
},
139-
[segmentValuesByValue]
127+
const segmentDimension = dimensions.find(
128+
(d) => d.iri === fields.segment?.componentIri
140129
);
141130

131+
const {
132+
getAbbreviationOrLabelByValue: getSegment,
133+
getLabelByAbbreviation: getSegmentLabel,
134+
abbreviationOrLabelLookup: segmentsByAbbreviationOrLabel,
135+
} = useMaybeAbbreviations({
136+
useAbbreviations: fields.segment?.useAbbreviations ?? false,
137+
dimension: segmentDimension,
138+
});
139+
140+
const segmentsByValue = useMemo(() => {
141+
const values = (segmentDimension?.values || []) as DimensionValue[];
142+
143+
return new Map(values.map((d) => [d.value, d]));
144+
}, [segmentDimension?.values]);
145+
146+
const showStandardError = get(fields, ["y", "showStandardError"], true);
147+
142148
// Sort
143149
const xSorting = fields.x.sorting;
144150

@@ -191,22 +197,20 @@ const useGroupedColumnsState = (
191197
const uniqueSegments = Array.from(
192198
new Set(plottableSortedData.map((d) => getSegment(d)))
193199
);
194-
const dimension = dimensions.find(
195-
(d) => d.iri === fields.segment?.componentIri
196-
);
197200

198201
const sorting = fields?.segment?.sorting;
199-
const sorters = makeDimensionValueSorters(dimension, {
202+
const sorters = makeDimensionValueSorters(segmentDimension, {
200203
sorting,
201204
sumsBySegment,
205+
useAbbreviations: fields.segment?.useAbbreviations,
202206
});
203207

204208
return orderBy(uniqueSegments, sorters, getSortingOrders(sorters, sorting));
205209
}, [
206210
plottableSortedData,
207-
dimensions,
211+
segmentDimension,
208212
fields.segment?.sorting,
209-
fields.segment?.componentIri,
213+
fields.segment?.useAbbreviations,
210214
sumsBySegment,
211215
getSegment,
212216
]);
@@ -228,10 +232,10 @@ const useGroupedColumnsState = (
228232

229233
if (fields.segment && segmentDimension && fields.segment.colorMapping) {
230234
const orderedSegmentLabelsAndColors = segments.map((segment) => {
231-
const dvIri = segmentDimension.values.find(
232-
(s: { label: string; value: string }) =>
233-
s.label === segment || s.value === segment
234-
).value;
235+
const dvIri =
236+
segmentsByAbbreviationOrLabel.get(segment)?.value ||
237+
segmentsByValue.get(segment)?.value ||
238+
"";
235239

236240
return {
237241
label: segment,
@@ -299,6 +303,8 @@ const useGroupedColumnsState = (
299303
fields.segment,
300304
preparedData,
301305
segments,
306+
segmentsByAbbreviationOrLabel,
307+
segmentsByValue,
302308
plottableSortedData,
303309
getX,
304310
getXAsDate,

0 commit comments

Comments
 (0)