diff --git a/packages/@ourworldindata/grapher/src/barCharts/DiscreteBarChart.tsx b/packages/@ourworldindata/grapher/src/barCharts/DiscreteBarChart.tsx index f53230fe812..fd3183fde78 100644 --- a/packages/@ourworldindata/grapher/src/barCharts/DiscreteBarChart.tsx +++ b/packages/@ourworldindata/grapher/src/barCharts/DiscreteBarChart.tsx @@ -138,6 +138,24 @@ export class DiscreteBarChart return table } + transformTableForSelection(table: OwidTable): OwidTable { + table = table + .replaceNonNumericCellsWithErrorValues(this.yColumnSlugs) + .dropRowsWithErrorValuesForAllColumns(this.yColumnSlugs) + + if (this.missingDataStrategy === MissingDataStrategy.hide) { + const groupedByEntity = table.groupBy("entityName").map((t) => { + if (t.hasAnyColumnNoValidValue(this.yColumnSlugs)) { + t = t.dropAllRows() + } + return t + }) + table = groupedByEntity[0].concat(groupedByEntity.slice(1)) + } + + return table + } + @computed get inputTable(): OwidTable { return this.manager.table } diff --git a/packages/@ourworldindata/grapher/src/chart/ChartInterface.ts b/packages/@ourworldindata/grapher/src/chart/ChartInterface.ts index b99f3d1b578..7cd9fd39b55 100644 --- a/packages/@ourworldindata/grapher/src/chart/ChartInterface.ts +++ b/packages/@ourworldindata/grapher/src/chart/ChartInterface.ts @@ -31,6 +31,7 @@ export interface ChartInterface { // Todo: should all charts additionally have a placedSeries: ChartPlacedSeries[] getter? transformTable: ChartTableTransformer + transformTableForSelection?: ChartTableTransformer yAxis?: HorizontalAxis | VerticalAxis xAxis?: HorizontalAxis | VerticalAxis diff --git a/packages/@ourworldindata/grapher/src/core/Grapher.tsx b/packages/@ourworldindata/grapher/src/core/Grapher.tsx index 98e8cf46123..5e1b2bf70a2 100644 --- a/packages/@ourworldindata/grapher/src/core/Grapher.tsx +++ b/packages/@ourworldindata/grapher/src/core/Grapher.tsx @@ -664,16 +664,21 @@ export class Grapher // Depending on the chart type, the criteria for being able to select an entity are // different; e.g. for scatterplots, the entity needs to (1) not be excluded and // (2) needs to have data for the x and y dimension. + let table = + this.isScatter || this.isSlopeChart + ? this.tableAfterAuthorTimelineAndActiveChartTransform + : this.inputTable + + if (!this.isReady) return table + + // Some chart types (e.g. stacked area charts) choose not to show an entity + // with incomplete data. Such chart types define a custom transform function + // to ensure that the entity selector only offers entities that are actually plotted. + if (this.chartInstance.transformTableForSelection) { + table = this.chartInstance.transformTableForSelection(table) + } - if (this.isScatter || this.isSlopeChart) - // for scatter and slope charts, the `transformTable()` call takes care of removing - // all entities that cannot be selected - return this.tableAfterAuthorTimelineAndActiveChartTransform - - // for other chart types, the `transformTable()` call would sometimes remove too many - // entities, and we want to use the inputTable instead (which should have exactly the - // entities where data is available) - return this.inputTable + return table } // If an author sets a timeline filter run it early in the pipeline so to the charts it's as if the filtered times do not exist diff --git a/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx b/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx index 756a80f8228..92b61700f68 100644 --- a/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx +++ b/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx @@ -300,6 +300,22 @@ export class LineChart return table } + transformTableForSelection(table: OwidTable): OwidTable { + table = table.replaceNonNumericCellsWithErrorValues(this.yColumnSlugs) + + if (this.missingDataStrategy === MissingDataStrategy.hide) { + const groupedByEntity = table.groupBy("entityName").map((t) => { + if (t.hasAnyColumnNoValidValue(this.yColumnSlugs)) { + t = t.dropAllRows() + } + return t + }) + table = groupedByEntity[0].concat(groupedByEntity.slice(1)) + } + + return table + } + @computed private get missingDataStrategy(): MissingDataStrategy { return this.manager.missingDataStrategy || MissingDataStrategy.auto } diff --git a/packages/@ourworldindata/grapher/src/stackedCharts/AbstractStackedChart.tsx b/packages/@ourworldindata/grapher/src/stackedCharts/AbstractStackedChart.tsx index 0da08a59baa..cfc48a56257 100644 --- a/packages/@ourworldindata/grapher/src/stackedCharts/AbstractStackedChart.tsx +++ b/packages/@ourworldindata/grapher/src/stackedCharts/AbstractStackedChart.tsx @@ -84,6 +84,29 @@ export class AbstractStackedChart return table } + transformTableForSelection(table: OwidTable): OwidTable { + table = table + .replaceNonNumericCellsWithErrorValues(this.yColumnSlugs) + .dropRowsWithErrorValuesForAllColumns(this.yColumnSlugs) + + if (this.shouldRunLinearInterpolation) { + this.yColumnSlugs.forEach((slug) => { + table = table.interpolateColumnLinearly(slug) + }) + } + + if (this.missingDataStrategy !== MissingDataStrategy.show) { + const groupedByEntity = table + .groupBy("entityName") + .map((t: OwidTable) => + t.dropRowsWithErrorValuesForAnyColumn(this.yColumnSlugs) + ) + table = groupedByEntity[0].concat(groupedByEntity.slice(1)) + } + + return table + } + @computed private get missingDataStrategy(): MissingDataStrategy { return this.manager.missingDataStrategy || MissingDataStrategy.auto } diff --git a/packages/@ourworldindata/grapher/src/stackedCharts/StackedDiscreteBarChart.tsx b/packages/@ourworldindata/grapher/src/stackedCharts/StackedDiscreteBarChart.tsx index 7c67cbe804e..04e97827615 100644 --- a/packages/@ourworldindata/grapher/src/stackedCharts/StackedDiscreteBarChart.tsx +++ b/packages/@ourworldindata/grapher/src/stackedCharts/StackedDiscreteBarChart.tsx @@ -142,6 +142,28 @@ export class StackedDiscreteBarChart return table } + transformTableForSelection(table: OwidTable): OwidTable { + table = table + .replaceNonNumericCellsWithErrorValues(this.yColumnSlugs) + .dropRowsWithErrorValuesForAllColumns(this.yColumnSlugs) + + this.yColumnSlugs.forEach((slug) => { + table = table.interpolateColumnWithTolerance(slug) + }) + + if (this.missingDataStrategy === MissingDataStrategy.hide) { + const groupedByEntity = table.groupBy("entityName").map((t) => { + if (t.hasAnyColumnNoValidValue(this.yColumnSlugs)) { + t = t.dropAllRows() + } + return t + }) + table = groupedByEntity[0].concat(groupedByEntity.slice(1)) + } + + return table + } + @computed get sortConfig(): SortConfig { return this.manager.sortConfig ?? {} }