@buildcanada/charts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +8 -0
- package/README.md +113 -0
- package/package.json +137 -0
- package/src/components/BodyPortal/BodyPortal.tsx +40 -0
- package/src/components/Button/Button.scss +110 -0
- package/src/components/Button/Button.tsx +101 -0
- package/src/components/Checkbox.scss +93 -0
- package/src/components/Checkbox.tsx +47 -0
- package/src/components/ExpandableToggle/ExpandableToggle.scss +123 -0
- package/src/components/ExpandableToggle/ExpandableToggle.tsx +60 -0
- package/src/components/GrapherTabIcon.tsx +156 -0
- package/src/components/GrapherTrendArrow.scss +16 -0
- package/src/components/GrapherTrendArrow.tsx +30 -0
- package/src/components/Halo/Halo.tsx +44 -0
- package/src/components/LabeledSwitch/LabeledSwitch.scss +109 -0
- package/src/components/LabeledSwitch/LabeledSwitch.tsx +62 -0
- package/src/components/MarkdownTextWrap/MarkdownTextWrap.tsx +1173 -0
- package/src/components/OverlayHeader.scss +18 -0
- package/src/components/OverlayHeader.tsx +29 -0
- package/src/components/RadioButton.scss +69 -0
- package/src/components/RadioButton.tsx +42 -0
- package/src/components/SimpleMarkdownText.tsx +89 -0
- package/src/components/TextInput.scss +17 -0
- package/src/components/TextInput.tsx +19 -0
- package/src/components/TextWrap/TextWrap.tsx +361 -0
- package/src/components/TextWrap/TextWrapUtils.ts +32 -0
- package/src/components/closeButton/CloseButton.scss +40 -0
- package/src/components/closeButton/CloseButton.tsx +27 -0
- package/src/components/index.ts +70 -0
- package/src/components/loadingIndicator/LoadingIndicator.scss +40 -0
- package/src/components/loadingIndicator/LoadingIndicator.tsx +28 -0
- package/src/components/markdown/remarkPlainLinks.ts +36 -0
- package/src/components/reactUtil.ts +20 -0
- package/src/components/stubs/CodeSnippet.tsx +19 -0
- package/src/components/stubs/DataCitation.tsx +16 -0
- package/src/components/stubs/IndicatorKeyData.tsx +45 -0
- package/src/components/stubs/IndicatorProcessing.tsx +15 -0
- package/src/components/stubs/IndicatorSources.tsx +15 -0
- package/src/components/styles/colors.scss +113 -0
- package/src/components/styles/mixins.scss +630 -0
- package/src/components/styles/typography.scss +579 -0
- package/src/components/styles/util.scss +89 -0
- package/src/components/styles/variables.scss +208 -0
- package/src/config/ChartsConfig.ts +163 -0
- package/src/config/ChartsProvider.tsx +157 -0
- package/src/config/index.ts +20 -0
- package/src/core-table/CoreTable.ts +1355 -0
- package/src/core-table/CoreTableColumns.ts +973 -0
- package/src/core-table/CoreTableUtils.ts +793 -0
- package/src/core-table/ErrorValues.ts +73 -0
- package/src/core-table/OwidTable.ts +1175 -0
- package/src/core-table/OwidTableSynthesizers.ts +272 -0
- package/src/core-table/OwidTableUtil.ts +76 -0
- package/src/core-table/Transforms.ts +484 -0
- package/src/core-table/index.ts +82 -0
- package/src/explorer/ColumnGrammar.ts +217 -0
- package/src/explorer/Explorer.sample.ts +212 -0
- package/src/explorer/Explorer.scss +148 -0
- package/src/explorer/Explorer.tsx +1283 -0
- package/src/explorer/ExplorerConstants.ts +85 -0
- package/src/explorer/ExplorerControls.scss +156 -0
- package/src/explorer/ExplorerControls.tsx +210 -0
- package/src/explorer/ExplorerDecisionMatrix.ts +471 -0
- package/src/explorer/ExplorerGrammar.ts +161 -0
- package/src/explorer/ExplorerProgram.ts +568 -0
- package/src/explorer/ExplorerUtils.ts +59 -0
- package/src/explorer/GrapherGrammar.ts +387 -0
- package/src/explorer/gridLang/GrammarUtils.ts +121 -0
- package/src/explorer/gridLang/GridCell.ts +298 -0
- package/src/explorer/gridLang/GridLangConstants.ts +255 -0
- package/src/explorer/gridLang/GridProgram.ts +311 -0
- package/src/explorer/gridLang/readme.md +17 -0
- package/src/explorer/index.ts +69 -0
- package/src/explorer/readme.md +19 -0
- package/src/explorer/urlMigrations/CO2UrlMigration.ts +46 -0
- package/src/explorer/urlMigrations/CovidUrlMigration.ts +37 -0
- package/src/explorer/urlMigrations/EnergyUrlMigration.ts +41 -0
- package/src/explorer/urlMigrations/ExplorerPageUrlMigrationSpec.ts +12 -0
- package/src/explorer/urlMigrations/ExplorerUrlMigrationUtils.ts +45 -0
- package/src/explorer/urlMigrations/ExplorerUrlMigrations.ts +33 -0
- package/src/explorer/urlMigrations/LegacyCovidUrlMigration.ts +144 -0
- package/src/explorer/urlMigrations/readme.md +39 -0
- package/src/grapher/axis/Axis.ts +973 -0
- package/src/grapher/axis/AxisConfig.ts +179 -0
- package/src/grapher/axis/AxisViews.tsx +597 -0
- package/src/grapher/barCharts/DiscreteBarChart.tsx +728 -0
- package/src/grapher/barCharts/DiscreteBarChartConstants.ts +60 -0
- package/src/grapher/barCharts/DiscreteBarChartHelpers.ts +338 -0
- package/src/grapher/barCharts/DiscreteBarChartState.ts +354 -0
- package/src/grapher/barCharts/DiscreteBarChartThumbnail.tsx +34 -0
- package/src/grapher/captionedChart/CaptionedChart.scss +61 -0
- package/src/grapher/captionedChart/CaptionedChart.tsx +523 -0
- package/src/grapher/captionedChart/Logos.tsx +141 -0
- package/src/grapher/captionedChart/LogosSVG.tsx +16 -0
- package/src/grapher/captionedChart/StaticChartRasterizer.tsx +178 -0
- package/src/grapher/captionedChart/assets/buildcanada-logo-square.svg +15 -0
- package/src/grapher/captionedChart/assets/buildcanada-logo.svg +15 -0
- package/src/grapher/captionedChart/assets/canadaspends.svg +7 -0
- package/src/grapher/captionedChart/readme.md +14 -0
- package/src/grapher/chart/Chart.tsx +62 -0
- package/src/grapher/chart/ChartAreaContent.tsx +172 -0
- package/src/grapher/chart/ChartDimension.ts +121 -0
- package/src/grapher/chart/ChartInterface.ts +83 -0
- package/src/grapher/chart/ChartManager.ts +113 -0
- package/src/grapher/chart/ChartTabs.ts +178 -0
- package/src/grapher/chart/ChartTypeMap.tsx +158 -0
- package/src/grapher/chart/ChartTypeSwitcher.tsx +26 -0
- package/src/grapher/chart/ChartUtils.tsx +364 -0
- package/src/grapher/chart/DimensionSlot.ts +45 -0
- package/src/grapher/chart/StaticChartWrapper.tsx +94 -0
- package/src/grapher/chart/guidedChartUtils.ts +82 -0
- package/src/grapher/color/BinningStrategies.ts +484 -0
- package/src/grapher/color/BinningStrategyEqualSizeBins.ts +132 -0
- package/src/grapher/color/BinningStrategyLogarithmic.ts +121 -0
- package/src/grapher/color/CategoricalColorAssigner.ts +97 -0
- package/src/grapher/color/ColorBrewerSchemes.ts +80 -0
- package/src/grapher/color/ColorConstants.ts +20 -0
- package/src/grapher/color/ColorScale.ts +339 -0
- package/src/grapher/color/ColorScaleBin.ts +147 -0
- package/src/grapher/color/ColorScaleConfig.ts +204 -0
- package/src/grapher/color/ColorScheme.ts +137 -0
- package/src/grapher/color/ColorSchemes.ts +149 -0
- package/src/grapher/color/ColorUtils.ts +86 -0
- package/src/grapher/color/CustomSchemes.ts +1772 -0
- package/src/grapher/color/readme.md +84 -0
- package/src/grapher/comparisonLine/ComparisonLine.tsx +31 -0
- package/src/grapher/comparisonLine/ComparisonLineConstants.ts +11 -0
- package/src/grapher/comparisonLine/ComparisonLineGenerator.ts +60 -0
- package/src/grapher/comparisonLine/ComparisonLineHelpers.ts +10 -0
- package/src/grapher/comparisonLine/CustomComparisonLine.tsx +159 -0
- package/src/grapher/comparisonLine/VerticalComparisonLine.tsx +208 -0
- package/src/grapher/controls/ActionButtons.scss +97 -0
- package/src/grapher/controls/ActionButtons.tsx +453 -0
- package/src/grapher/controls/CommandPalette.scss +50 -0
- package/src/grapher/controls/CommandPalette.tsx +74 -0
- package/src/grapher/controls/ContentSwitchers.scss +93 -0
- package/src/grapher/controls/ContentSwitchers.tsx +238 -0
- package/src/grapher/controls/Controls.scss +158 -0
- package/src/grapher/controls/DataTableFilterDropdown.scss +7 -0
- package/src/grapher/controls/DataTableFilterDropdown.tsx +168 -0
- package/src/grapher/controls/DataTableSearchField.scss +3 -0
- package/src/grapher/controls/DataTableSearchField.tsx +76 -0
- package/src/grapher/controls/Dropdown.scss +252 -0
- package/src/grapher/controls/Dropdown.tsx +235 -0
- package/src/grapher/controls/EntitySelectionToggle.tsx +135 -0
- package/src/grapher/controls/MapRegionDropdown.scss +3 -0
- package/src/grapher/controls/MapRegionDropdown.tsx +104 -0
- package/src/grapher/controls/MapResetButton.tsx +115 -0
- package/src/grapher/controls/MapZoomDropdown.scss +9 -0
- package/src/grapher/controls/MapZoomDropdown.tsx +270 -0
- package/src/grapher/controls/MapZoomToSelectionButton.tsx +87 -0
- package/src/grapher/controls/SearchField.scss +78 -0
- package/src/grapher/controls/SearchField.tsx +63 -0
- package/src/grapher/controls/SettingsMenu.scss +191 -0
- package/src/grapher/controls/SettingsMenu.tsx +399 -0
- package/src/grapher/controls/ShareMenu.scss +58 -0
- package/src/grapher/controls/ShareMenu.tsx +304 -0
- package/src/grapher/controls/SortIcon.tsx +39 -0
- package/src/grapher/controls/VerticalScrollContainer.tsx +263 -0
- package/src/grapher/controls/controlsRow/ControlsRow.tsx +168 -0
- package/src/grapher/controls/dropdown-icons.scss +4 -0
- package/src/grapher/controls/entityPicker/EntityPicker.scss +255 -0
- package/src/grapher/controls/entityPicker/EntityPicker.tsx +816 -0
- package/src/grapher/controls/entityPicker/EntityPickerConstants.ts +23 -0
- package/src/grapher/controls/globalEntitySelector/GlobalEntitySelector.scss +129 -0
- package/src/grapher/controls/globalEntitySelector/GlobalEntitySelector.tsx +463 -0
- package/src/grapher/controls/globalEntitySelector/GlobalEntitySelectorConstants.ts +3 -0
- package/src/grapher/controls/globalEntitySelector/readme.md +17 -0
- package/src/grapher/controls/settings/AbsRelToggle.tsx +64 -0
- package/src/grapher/controls/settings/AxisScaleToggle.tsx +53 -0
- package/src/grapher/controls/settings/FacetStrategySelector.tsx +110 -0
- package/src/grapher/controls/settings/FacetYDomainToggle.tsx +51 -0
- package/src/grapher/controls/settings/NoDataAreaToggle.tsx +38 -0
- package/src/grapher/controls/settings/ZoomToggle.tsx +36 -0
- package/src/grapher/core/EntitiesByRegionType.ts +174 -0
- package/src/grapher/core/EntityCodes.ts +19 -0
- package/src/grapher/core/EntityUrlBuilder.ts +200 -0
- package/src/grapher/core/FetchingGrapher.tsx +156 -0
- package/src/grapher/core/Grapher.tsx +760 -0
- package/src/grapher/core/GrapherAnalytics.ts +229 -0
- package/src/grapher/core/GrapherConstants.ts +173 -0
- package/src/grapher/core/GrapherState.tsx +3659 -0
- package/src/grapher/core/GrapherUrl.ts +184 -0
- package/src/grapher/core/GrapherUrlMigrations.ts +29 -0
- package/src/grapher/core/GrapherUseHelpers.tsx +147 -0
- package/src/grapher/core/LegacyToOwidTable.ts +841 -0
- package/src/grapher/core/grapher.entry.ts +5 -0
- package/src/grapher/core/grapher.scss +257 -0
- package/src/grapher/core/loadGrapherTableHelpers.ts +116 -0
- package/src/grapher/core/loadVariable.ts +104 -0
- package/src/grapher/core/relatedQuestion.ts +12 -0
- package/src/grapher/core/typography.scss +206 -0
- package/src/grapher/dataTable/DataTable.sample.ts +206 -0
- package/src/grapher/dataTable/DataTable.scss +249 -0
- package/src/grapher/dataTable/DataTable.tsx +1332 -0
- package/src/grapher/dataTable/DataTableConstants.ts +186 -0
- package/src/grapher/entitySelector/EntitySelector.scss +255 -0
- package/src/grapher/entitySelector/EntitySelector.tsx +1838 -0
- package/src/grapher/facet/FacetChart.tsx +943 -0
- package/src/grapher/facet/FacetChartConstants.ts +24 -0
- package/src/grapher/facet/FacetChartUtils.ts +51 -0
- package/src/grapher/facet/FacetMap.tsx +604 -0
- package/src/grapher/facet/FacetMapConstants.ts +23 -0
- package/src/grapher/facet/readme.md +13 -0
- package/src/grapher/focus/FocusArray.ts +79 -0
- package/src/grapher/footer/Footer.scss +63 -0
- package/src/grapher/footer/Footer.tsx +809 -0
- package/src/grapher/footer/FooterManager.ts +44 -0
- package/src/grapher/fullScreen/FullScreen.scss +11 -0
- package/src/grapher/fullScreen/FullScreen.tsx +61 -0
- package/src/grapher/header/Header.scss +35 -0
- package/src/grapher/header/Header.tsx +372 -0
- package/src/grapher/header/HeaderManager.ts +28 -0
- package/src/grapher/index.ts +157 -0
- package/src/grapher/interaction/InteractionState.ts +60 -0
- package/src/grapher/legend/HorizontalColorLegends.tsx +923 -0
- package/src/grapher/legend/LegendInteractionState.ts +40 -0
- package/src/grapher/legend/VerticalColorLegend.tsx +295 -0
- package/src/grapher/lineCharts/LineChart.tsx +968 -0
- package/src/grapher/lineCharts/LineChartConstants.ts +89 -0
- package/src/grapher/lineCharts/LineChartHelpers.ts +184 -0
- package/src/grapher/lineCharts/LineChartState.ts +394 -0
- package/src/grapher/lineCharts/LineChartThumbnail.tsx +437 -0
- package/src/grapher/lineCharts/Lines.tsx +258 -0
- package/src/grapher/lineLegend/LineLegend.tsx +723 -0
- package/src/grapher/lineLegend/LineLegendConstants.ts +9 -0
- package/src/grapher/lineLegend/LineLegendFilterAlgorithms.ts +143 -0
- package/src/grapher/lineLegend/LineLegendHelpers.ts +253 -0
- package/src/grapher/lineLegend/LineLegendTypes.ts +32 -0
- package/src/grapher/mapCharts/CanadaTopology.ts +17922 -0
- package/src/grapher/mapCharts/ChoroplethGlobe.tsx +949 -0
- package/src/grapher/mapCharts/ChoroplethMap.tsx +662 -0
- package/src/grapher/mapCharts/GeoFeatures.ts +184 -0
- package/src/grapher/mapCharts/GlobeController.ts +496 -0
- package/src/grapher/mapCharts/MapAnnotationPlacements.json +1040 -0
- package/src/grapher/mapCharts/MapAnnotationPlacements.ts +31 -0
- package/src/grapher/mapCharts/MapAnnotations.ts +723 -0
- package/src/grapher/mapCharts/MapChart.sample.ts +59 -0
- package/src/grapher/mapCharts/MapChart.scss +5 -0
- package/src/grapher/mapCharts/MapChart.tsx +720 -0
- package/src/grapher/mapCharts/MapChartConstants.ts +260 -0
- package/src/grapher/mapCharts/MapChartState.ts +416 -0
- package/src/grapher/mapCharts/MapChartThumbnail.tsx +25 -0
- package/src/grapher/mapCharts/MapComponents.tsx +338 -0
- package/src/grapher/mapCharts/MapConfig.ts +156 -0
- package/src/grapher/mapCharts/MapHelpers.ts +181 -0
- package/src/grapher/mapCharts/MapProjections.ts +49 -0
- package/src/grapher/mapCharts/MapSparkline.tsx +257 -0
- package/src/grapher/mapCharts/MapTooltip.scss +49 -0
- package/src/grapher/mapCharts/MapTooltip.tsx +409 -0
- package/src/grapher/mapCharts/MapTopology.ts +1766 -0
- package/src/grapher/mapCharts/d3-bboxCollide.js +204 -0
- package/src/grapher/mapCharts/d3-geo-projection.ts +198 -0
- package/src/grapher/modal/DownloadIcons.tsx +39 -0
- package/src/grapher/modal/DownloadModal.scss +300 -0
- package/src/grapher/modal/DownloadModal.tsx +1226 -0
- package/src/grapher/modal/EmbedModal.scss +40 -0
- package/src/grapher/modal/EmbedModal.tsx +160 -0
- package/src/grapher/modal/EntitySelectorModal.tsx +59 -0
- package/src/grapher/modal/Modal.scss +31 -0
- package/src/grapher/modal/Modal.tsx +90 -0
- package/src/grapher/modal/ModalHeader.scss +12 -0
- package/src/grapher/modal/ModalHeader.tsx +16 -0
- package/src/grapher/modal/SourcesDescriptions.scss +87 -0
- package/src/grapher/modal/SourcesDescriptions.tsx +89 -0
- package/src/grapher/modal/SourcesKeyDataTable.scss +49 -0
- package/src/grapher/modal/SourcesKeyDataTable.tsx +87 -0
- package/src/grapher/modal/SourcesModal.scss +301 -0
- package/src/grapher/modal/SourcesModal.tsx +568 -0
- package/src/grapher/noDataModal/NoDataModal.tsx +125 -0
- package/src/grapher/scatterCharts/ConnectedScatterLegend.tsx +143 -0
- package/src/grapher/scatterCharts/MultiColorPolyline.tsx +129 -0
- package/src/grapher/scatterCharts/NoDataSection.scss +14 -0
- package/src/grapher/scatterCharts/NoDataSection.tsx +56 -0
- package/src/grapher/scatterCharts/ScatterPlotChart.tsx +792 -0
- package/src/grapher/scatterCharts/ScatterPlotChartConstants.ts +157 -0
- package/src/grapher/scatterCharts/ScatterPlotChartState.ts +678 -0
- package/src/grapher/scatterCharts/ScatterPlotChartThumbnail.tsx +155 -0
- package/src/grapher/scatterCharts/ScatterPlotTooltip.tsx +560 -0
- package/src/grapher/scatterCharts/ScatterPoints.tsx +153 -0
- package/src/grapher/scatterCharts/ScatterPointsWithLabels.tsx +708 -0
- package/src/grapher/scatterCharts/ScatterSizeLegend.tsx +327 -0
- package/src/grapher/scatterCharts/ScatterUtils.ts +265 -0
- package/src/grapher/scatterCharts/Triangle.tsx +41 -0
- package/src/grapher/schema/README.md +33 -0
- package/src/grapher/schema/defaultGrapherConfig.ts +100 -0
- package/src/grapher/schema/grapher-schema.009.yaml +781 -0
- package/src/grapher/schema/migrations/helpers.ts +58 -0
- package/src/grapher/schema/migrations/migrate.ts +75 -0
- package/src/grapher/schema/migrations/migrations.ts +158 -0
- package/src/grapher/selection/MapSelectionArray.ts +99 -0
- package/src/grapher/selection/SelectionArray.ts +71 -0
- package/src/grapher/selection/readme.md +16 -0
- package/src/grapher/sidePanel/SidePanel.scss +10 -0
- package/src/grapher/sidePanel/SidePanel.tsx +23 -0
- package/src/grapher/slideInDrawer/SlideInDrawer.scss +57 -0
- package/src/grapher/slideInDrawer/SlideInDrawer.tsx +125 -0
- package/src/grapher/slideshowController/SlideShowController.tsx +43 -0
- package/src/grapher/slideshowController/readme.md +7 -0
- package/src/grapher/slopeCharts/MarkX.tsx +45 -0
- package/src/grapher/slopeCharts/Slope.tsx +102 -0
- package/src/grapher/slopeCharts/SlopeChart.tsx +1152 -0
- package/src/grapher/slopeCharts/SlopeChartConstants.ts +33 -0
- package/src/grapher/slopeCharts/SlopeChartHelpers.ts +73 -0
- package/src/grapher/slopeCharts/SlopeChartState.ts +392 -0
- package/src/grapher/slopeCharts/SlopeChartThumbnail.tsx +368 -0
- package/src/grapher/stackedCharts/AbstractStackedChartState.ts +370 -0
- package/src/grapher/stackedCharts/MarimekkoBars.tsx +190 -0
- package/src/grapher/stackedCharts/MarimekkoBarsForOneEntity.tsx +168 -0
- package/src/grapher/stackedCharts/MarimekkoChart.tsx +1144 -0
- package/src/grapher/stackedCharts/MarimekkoChartConstants.ts +112 -0
- package/src/grapher/stackedCharts/MarimekkoChartHelpers.ts +21 -0
- package/src/grapher/stackedCharts/MarimekkoChartState.ts +465 -0
- package/src/grapher/stackedCharts/MarimekkoChartThumbnail.tsx +168 -0
- package/src/grapher/stackedCharts/MarimekkoInternalLabels.tsx +124 -0
- package/src/grapher/stackedCharts/StackedAreaChart.tsx +678 -0
- package/src/grapher/stackedCharts/StackedAreaChartState.ts +34 -0
- package/src/grapher/stackedCharts/StackedAreaChartThumbnail.tsx +215 -0
- package/src/grapher/stackedCharts/StackedAreas.tsx +223 -0
- package/src/grapher/stackedCharts/StackedBarChart.tsx +619 -0
- package/src/grapher/stackedCharts/StackedBarChartState.ts +80 -0
- package/src/grapher/stackedCharts/StackedBarChartThumbnail.tsx +220 -0
- package/src/grapher/stackedCharts/StackedBarSegment.tsx +87 -0
- package/src/grapher/stackedCharts/StackedBars.tsx +102 -0
- package/src/grapher/stackedCharts/StackedConstants.ts +109 -0
- package/src/grapher/stackedCharts/StackedDiscreteBarChart.tsx +270 -0
- package/src/grapher/stackedCharts/StackedDiscreteBarChartState.ts +296 -0
- package/src/grapher/stackedCharts/StackedDiscreteBarChartThumbnail.tsx +27 -0
- package/src/grapher/stackedCharts/StackedDiscreteBars.tsx +648 -0
- package/src/grapher/stackedCharts/StackedUtils.ts +142 -0
- package/src/grapher/tabs/Tabs.scss +169 -0
- package/src/grapher/tabs/Tabs.tsx +54 -0
- package/src/grapher/tabs/TabsWithDropdown.scss +62 -0
- package/src/grapher/tabs/TabsWithDropdown.tsx +114 -0
- package/src/grapher/testData/OwidTestData.sample.ts +273 -0
- package/src/grapher/testData/OwidTestData.ts +64 -0
- package/src/grapher/timeline/TimelineComponent.scss +139 -0
- package/src/grapher/timeline/TimelineComponent.tsx +658 -0
- package/src/grapher/timeline/TimelineController.ts +368 -0
- package/src/grapher/timeline/readme.md +7 -0
- package/src/grapher/tooltip/Tooltip.scss +510 -0
- package/src/grapher/tooltip/Tooltip.tsx +294 -0
- package/src/grapher/tooltip/TooltipContents.tsx +383 -0
- package/src/grapher/tooltip/TooltipProps.ts +123 -0
- package/src/grapher/tooltip/TooltipState.ts +81 -0
- package/src/grapher/verticalLabels/VerticalLabels.tsx +31 -0
- package/src/grapher/verticalLabels/VerticalLabelsState.ts +154 -0
- package/src/index.ts +226 -0
- package/src/styles/charts.scss +15 -0
- package/src/types/NominalType.ts +30 -0
- package/src/types/OwidOrigin.ts +18 -0
- package/src/types/OwidSource.ts +9 -0
- package/src/types/OwidVariable.ts +133 -0
- package/src/types/OwidVariableDisplayConfigInterface.ts +49 -0
- package/src/types/analyticsTypes.ts +54 -0
- package/src/types/dbTypes/Tags.ts +11 -0
- package/src/types/domainTypes/Archive.ts +139 -0
- package/src/types/domainTypes/Author.ts +28 -0
- package/src/types/domainTypes/ContentGraph.ts +76 -0
- package/src/types/domainTypes/CoreTableTypes.ts +305 -0
- package/src/types/domainTypes/DeployStatus.ts +23 -0
- package/src/types/domainTypes/Layout.ts +34 -0
- package/src/types/domainTypes/Posts.ts +34 -0
- package/src/types/domainTypes/Search.ts +299 -0
- package/src/types/domainTypes/Site.ts +8 -0
- package/src/types/domainTypes/StaticViz.ts +64 -0
- package/src/types/domainTypes/Toc.ts +11 -0
- package/src/types/domainTypes/Tombstone.ts +19 -0
- package/src/types/domainTypes/Various.ts +79 -0
- package/src/types/gdocTypes/Gdoc.ts +280 -0
- package/src/types/grapherTypes/BinningStrategyTypes.ts +46 -0
- package/src/types/grapherTypes/GrapherConstants.ts +53 -0
- package/src/types/grapherTypes/GrapherTypes.ts +743 -0
- package/src/types/index.ts +316 -0
- package/src/types/wordpressTypes/WordpressTypes.ts +9 -0
- package/src/utils/Bounds.ts +439 -0
- package/src/utils/BrowserUtils.ts +12 -0
- package/src/utils/FuzzySearch.ts +74 -0
- package/src/utils/MultiDimDataPageConfig.ts +31 -0
- package/src/utils/OwidVariable.ts +82 -0
- package/src/utils/PointVector.ts +97 -0
- package/src/utils/PromiseCache.ts +36 -0
- package/src/utils/PromiseSwitcher.ts +52 -0
- package/src/utils/TimeBounds.ts +130 -0
- package/src/utils/Tippy.tsx +57 -0
- package/src/utils/Util.ts +2369 -0
- package/src/utils/archival/archivalDate.ts +48 -0
- package/src/utils/dayjs.ts +32 -0
- package/src/utils/formatValue.ts +242 -0
- package/src/utils/grapherConfigUtils.ts +81 -0
- package/src/utils/image.ts +225 -0
- package/src/utils/index.ts +318 -0
- package/src/utils/isPresent.ts +5 -0
- package/src/utils/metadataHelpers.ts +329 -0
- package/src/utils/persistable/Persistable.ts +82 -0
- package/src/utils/persistable/readme.md +50 -0
- package/src/utils/regions.json +5635 -0
- package/src/utils/regions.ts +463 -0
- package/src/utils/serializers.ts +16 -0
- package/src/utils/string.ts +42 -0
- package/src/utils/urls/Url.ts +195 -0
- package/src/utils/urls/UrlMigration.ts +10 -0
- package/src/utils/urls/UrlUtils.ts +54 -0
- package/src/utils/urls/readme.md +90 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import * as _ from "lodash-es"
|
|
2
|
+
import * as R from "remeda"
|
|
3
|
+
import {
|
|
4
|
+
CoreColumn,
|
|
5
|
+
defaultIfErrorValue,
|
|
6
|
+
isNotErrorValue,
|
|
7
|
+
OwidTable,
|
|
8
|
+
} from "../../core-table/index.js"
|
|
9
|
+
import { ChartState } from "../chart/ChartInterface"
|
|
10
|
+
import { ColorScale, ColorScaleManager } from "../color/ColorScale"
|
|
11
|
+
import {
|
|
12
|
+
SCATTER_POINT_DEFAULT_COLOR,
|
|
13
|
+
ScatterPlotManager,
|
|
14
|
+
ScatterSeries,
|
|
15
|
+
SeriesPoint,
|
|
16
|
+
} from "./ScatterPlotChartConstants"
|
|
17
|
+
import { computed, makeObservable } from "mobx"
|
|
18
|
+
import {
|
|
19
|
+
autoDetectYColumnSlugs,
|
|
20
|
+
getShortNameForEntity,
|
|
21
|
+
makeSelectionArray,
|
|
22
|
+
} from "../chart/ChartUtils"
|
|
23
|
+
import {
|
|
24
|
+
ChartErrorInfo,
|
|
25
|
+
ColorSchemeName,
|
|
26
|
+
EntityName,
|
|
27
|
+
ScaleType,
|
|
28
|
+
ScatterPointLabelStrategy,
|
|
29
|
+
ColorScaleConfigInterface,
|
|
30
|
+
SeriesName,
|
|
31
|
+
ValueRange,
|
|
32
|
+
} from "../../types/index.js"
|
|
33
|
+
import {
|
|
34
|
+
domainExtent,
|
|
35
|
+
intersection,
|
|
36
|
+
lowerCaseFirstLetterUnlessAbbreviation,
|
|
37
|
+
} from "../../utils/index.js"
|
|
38
|
+
import { ColorScaleConfig } from "../color/ColorScaleConfig"
|
|
39
|
+
import { OWID_NO_DATA_GRAY } from "../color/ColorConstants"
|
|
40
|
+
import { AxisConfig } from "../axis/AxisConfig"
|
|
41
|
+
import { BASE_FONT_SIZE } from "../core/GrapherConstants"
|
|
42
|
+
import { SelectionArray } from "../selection/SelectionArray"
|
|
43
|
+
import { computeSizeDomain } from "./ScatterUtils"
|
|
44
|
+
import { FocusArray } from "../focus/FocusArray"
|
|
45
|
+
import { HorizontalAxis, VerticalAxis } from "../axis/Axis.js"
|
|
46
|
+
|
|
47
|
+
export class ScatterPlotChartState implements ChartState, ColorScaleManager {
|
|
48
|
+
manager: ScatterPlotManager
|
|
49
|
+
|
|
50
|
+
colorScale: ColorScale
|
|
51
|
+
defaultBaseColorScheme = ColorSchemeName.continents
|
|
52
|
+
defaultNoDataColor = OWID_NO_DATA_GRAY
|
|
53
|
+
|
|
54
|
+
constructor({ manager }: { manager: ScatterPlotManager }) {
|
|
55
|
+
this.manager = manager
|
|
56
|
+
this.colorScale = manager.colorScaleOverride ?? new ColorScale(this)
|
|
57
|
+
makeObservable(this)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@computed get inputTable(): OwidTable {
|
|
61
|
+
return this.manager.table
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@computed get transformedTableFromGrapher(): OwidTable {
|
|
65
|
+
return (
|
|
66
|
+
this.manager.transformedTable ??
|
|
67
|
+
this.transformTable(this.inputTable)
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// TODO chunk this up into multiple computeds for better performance?
|
|
72
|
+
@computed get transformedTable(): OwidTable {
|
|
73
|
+
let table = this.transformedTableFromGrapher
|
|
74
|
+
// We don't want to apply this transform when relative mode is also enabled, it has a
|
|
75
|
+
// slightly different endpoints logic that drops initial zeroes to avoid DivideByZero error.
|
|
76
|
+
if (this.compareEndPointsOnly && !this.manager.isRelativeMode) {
|
|
77
|
+
table = table.keepMinTimeAndMaxTimeForEachEntityOnly()
|
|
78
|
+
}
|
|
79
|
+
if (this.manager.isRelativeMode) {
|
|
80
|
+
table = table.toAverageAnnualChangeForEachEntity([
|
|
81
|
+
this.xColumnSlug,
|
|
82
|
+
this.yColumnSlug,
|
|
83
|
+
])
|
|
84
|
+
}
|
|
85
|
+
return table
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
transformTable(table: OwidTable): OwidTable {
|
|
89
|
+
// Drop all entities that have no data in either the X or Y column.
|
|
90
|
+
// For some charts, this can drop more than 50% of rows, so we do it first.
|
|
91
|
+
// If there's no data at all for an entity, then tolerance can also not "recover" any data, so this is safe to do.
|
|
92
|
+
table = table.dropEntitiesThatHaveNoDataInSomeColumn([
|
|
93
|
+
this.xColumnSlug,
|
|
94
|
+
this.yColumnSlug,
|
|
95
|
+
])
|
|
96
|
+
|
|
97
|
+
if (this.xScaleType === ScaleType.log && this.xColumnSlug)
|
|
98
|
+
table = table.replaceNonPositiveCellsForLogScale([this.xColumnSlug])
|
|
99
|
+
|
|
100
|
+
if (this.yScaleType === ScaleType.log && this.yColumnSlug)
|
|
101
|
+
table = table.replaceNonPositiveCellsForLogScale([this.yColumnSlug])
|
|
102
|
+
|
|
103
|
+
if (this.colorColumnSlug && this.manager.matchingEntitiesOnly)
|
|
104
|
+
table = table.dropRowsWithErrorValuesForColumn(this.colorColumnSlug)
|
|
105
|
+
|
|
106
|
+
// We want to "chop off" any rows outside the time domain for X and Y to avoid creating
|
|
107
|
+
// leading and trailing timeline times that don't really exist in the dataset.
|
|
108
|
+
const [timeDomainStart, timeDomainEnd] = table.timeDomainFor([
|
|
109
|
+
this.xColumnSlug,
|
|
110
|
+
this.yColumnSlug,
|
|
111
|
+
])
|
|
112
|
+
table = table.filterByTimeRange(
|
|
113
|
+
timeDomainStart ?? -Infinity,
|
|
114
|
+
timeDomainEnd ?? Infinity
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if (this.xOverrideTime !== undefined) {
|
|
118
|
+
table = table.interpolateColumnWithTolerance(this.yColumnSlug)
|
|
119
|
+
} else {
|
|
120
|
+
table = table.interpolateColumnsByClosestTimeMatch(
|
|
121
|
+
this.xColumnSlug,
|
|
122
|
+
this.yColumnSlug
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Drop any rows which have non-number values for X or Y.
|
|
127
|
+
// This needs to be done after the tolerance, because the tolerance may fill in some gaps.
|
|
128
|
+
table = table
|
|
129
|
+
.columnFilter(
|
|
130
|
+
this.xColumnSlug,
|
|
131
|
+
_.isNumber,
|
|
132
|
+
"Drop rows with non-number values in X column"
|
|
133
|
+
)
|
|
134
|
+
.columnFilter(
|
|
135
|
+
this.yColumnSlug,
|
|
136
|
+
_.isNumber,
|
|
137
|
+
"Drop rows with non-number values in Y column"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
// The tolerance application might lead to some data being dropped for some years.
|
|
141
|
+
// For example, if X times are [2000, 2005, 2010], and Y times are [2005], then for all 3
|
|
142
|
+
// rows we have the same match [[2005, 2005], [2005, 2005], [2005, 2005]].
|
|
143
|
+
// This means we can drop 2000 and 2010 from the timeline.
|
|
144
|
+
// It might not make a huge difference here, but it makes a difference when there are more
|
|
145
|
+
// entities covering different time periods.
|
|
146
|
+
const [originalTimeDomainStart, originalTimeDomainEnd] =
|
|
147
|
+
table.originalTimeDomainFor([this.xColumnSlug, this.yColumnSlug])
|
|
148
|
+
table = table.filterByTimeRange(
|
|
149
|
+
originalTimeDomainStart ?? -Infinity,
|
|
150
|
+
originalTimeDomainEnd ?? Infinity
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return table
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
transformTableForDisplay(table: OwidTable): OwidTable {
|
|
157
|
+
// Drop any rows which have non-number values for X or Y.
|
|
158
|
+
table = table
|
|
159
|
+
.columnFilter(
|
|
160
|
+
this.xColumnSlug,
|
|
161
|
+
_.isNumber,
|
|
162
|
+
"Drop rows with non-number values in X column"
|
|
163
|
+
)
|
|
164
|
+
.columnFilter(
|
|
165
|
+
this.yColumnSlug,
|
|
166
|
+
_.isNumber,
|
|
167
|
+
"Drop rows with non-number values in Y column"
|
|
168
|
+
)
|
|
169
|
+
return table
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@computed get selectionArray(): SelectionArray {
|
|
173
|
+
return makeSelectionArray(this.manager.selection)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@computed get focusArray(): FocusArray {
|
|
177
|
+
return this.manager.focusArray ?? new FocusArray()
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@computed get isFocusModeActive(): boolean {
|
|
181
|
+
return this.focusArray.hasFocusedSeries
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// todo: remove. do this at table filter level
|
|
185
|
+
@computed get seriesNamesToHighlight(): Set<SeriesName> {
|
|
186
|
+
const seriesNames = this.selectionArray.selectedEntityNames
|
|
187
|
+
|
|
188
|
+
if (this.manager.matchingEntitiesOnly && !this.colorColumn.isMissing)
|
|
189
|
+
return new Set(
|
|
190
|
+
intersection(seriesNames, this.colorColumn.uniqEntityNames)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return new Set(seriesNames)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// todo: remove this. Should be done as a simple column transform at the data level.
|
|
197
|
+
// Possible to override the x axis dimension to target a special year
|
|
198
|
+
// In case you want to graph say, education in the past and democracy today https://ourworldindata.org/grapher/correlation-between-education-and-democracy
|
|
199
|
+
@computed get xOverrideTime(): number | undefined {
|
|
200
|
+
return this.manager.xOverrideTime
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
@computed get compareEndPointsOnly(): boolean {
|
|
204
|
+
return !!this.manager.compareEndPointsOnly
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@computed get xScaleType(): ScaleType {
|
|
208
|
+
return this.manager.isRelativeMode
|
|
209
|
+
? ScaleType.linear
|
|
210
|
+
: (this.manager.xAxisConfig?.scaleType ?? ScaleType.linear)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@computed get yScaleType(): ScaleType {
|
|
214
|
+
return this.manager.isRelativeMode
|
|
215
|
+
? ScaleType.linear
|
|
216
|
+
: (this.manager.yAxisConfig?.scaleType ?? ScaleType.linear)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
@computed get yColumnSlug(): string {
|
|
220
|
+
return autoDetectYColumnSlugs(this.manager)[0]
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@computed get yColumn(): CoreColumn {
|
|
224
|
+
return this.transformedTable.get(this.yColumnSlug)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@computed get xColumnSlug(): string {
|
|
228
|
+
const { xColumnSlug } = this.manager
|
|
229
|
+
return xColumnSlug ?? this.manager.table.timeColumn.slug
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
@computed get isTimeScatter(): boolean {
|
|
233
|
+
return this.manager.xColumnSlug === undefined
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
@computed get xColumn(): CoreColumn {
|
|
237
|
+
return this.transformedTable.get(this.xColumnSlug)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@computed get sizeColumnSlug(): string | undefined {
|
|
241
|
+
return this.manager.sizeColumnSlug
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@computed get sizeColumn(): CoreColumn {
|
|
245
|
+
return this.transformedTable.get(this.sizeColumnSlug)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@computed get colorColumnSlug(): string | undefined {
|
|
249
|
+
// Scatter plots only support categorical variables as color dimension
|
|
250
|
+
return this.manager.categoricalColorColumnSlug
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@computed get colorColumn(): CoreColumn {
|
|
254
|
+
return this.transformedTable.get(this.colorColumnSlug)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@computed get colorScaleColumn(): CoreColumn {
|
|
258
|
+
return (
|
|
259
|
+
// For faceted charts, we have to get the values of inputTable before it's filtered by
|
|
260
|
+
// the faceting logic.
|
|
261
|
+
this.manager.colorScaleColumnOverride ?? // We need to use inputTable in order to get consistent coloring for a variable across
|
|
262
|
+
// charts, e.g. each continent being assigned to the same color.
|
|
263
|
+
// inputTable is unfiltered, so it contains every value that exists in the variable.
|
|
264
|
+
this.inputTable.get(this.colorColumnSlug)
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@computed get colorScaleConfig(): ColorScaleConfigInterface | undefined {
|
|
269
|
+
return (
|
|
270
|
+
ColorScaleConfig.fromDSL(this.colorColumn.def) ??
|
|
271
|
+
this.manager.colorScale
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@computed get hasNoDataBin(): boolean {
|
|
276
|
+
if (this.colorColumn.isMissing) return false
|
|
277
|
+
return this.colorColumn.valuesIncludingErrorValues.some(
|
|
278
|
+
(value) => !isNotErrorValue(value)
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
@computed private get allEntityNamesWithXAndY(): EntityName[] {
|
|
283
|
+
return intersection(
|
|
284
|
+
this.yColumn.uniqEntityNames,
|
|
285
|
+
this.xColumn.uniqEntityNames
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private assignColorToSeries(
|
|
290
|
+
entityName: EntityName,
|
|
291
|
+
series: ScatterSeries
|
|
292
|
+
): void {
|
|
293
|
+
if (series.points.length) {
|
|
294
|
+
const keyColor =
|
|
295
|
+
this.transformedTable.getColorForEntityName(entityName)
|
|
296
|
+
if (keyColor !== undefined) series.color = keyColor
|
|
297
|
+
else if (!this.colorColumn.isMissing) {
|
|
298
|
+
const colorValue = R.last(series.points)?.color
|
|
299
|
+
const color = this.colorScale.getColor(colorValue)
|
|
300
|
+
if (color !== undefined) {
|
|
301
|
+
series.color = color
|
|
302
|
+
series.isScaleColor = true
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
@computed get fontSize(): number {
|
|
309
|
+
return this.manager.fontSize ?? BASE_FONT_SIZE
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@computed private get xAxisConfig(): AxisConfig {
|
|
313
|
+
return new AxisConfig(this.manager.xAxisConfig, this)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
@computed private get yAxisConfig(): AxisConfig {
|
|
317
|
+
return new AxisConfig(this.manager.yAxisConfig, this)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private removePointsOutsidePlane(points: SeriesPoint[]): SeriesPoint[] {
|
|
321
|
+
const { xAxisConfig, yAxisConfig } = this
|
|
322
|
+
|
|
323
|
+
if (
|
|
324
|
+
yAxisConfig.removePointsOutsideDomain ||
|
|
325
|
+
xAxisConfig.removePointsOutsideDomain
|
|
326
|
+
) {
|
|
327
|
+
return points.filter((point) => {
|
|
328
|
+
return (
|
|
329
|
+
!xAxisConfig.shouldRemovePoint(point.x) &&
|
|
330
|
+
!yAxisConfig.shouldRemovePoint(point.y)
|
|
331
|
+
)
|
|
332
|
+
})
|
|
333
|
+
}
|
|
334
|
+
return points
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private getPointLabel(rowIndex: number): string | undefined {
|
|
338
|
+
const strat = this.manager.scatterPointLabelStrategy
|
|
339
|
+
const { xColumn, yColumn } = this
|
|
340
|
+
const { timeColumn } = this.transformedTable
|
|
341
|
+
let label
|
|
342
|
+
if (strat === ScatterPointLabelStrategy.y) {
|
|
343
|
+
label = yColumn?.formatValue(
|
|
344
|
+
yColumn.valuesIncludingErrorValues[rowIndex]
|
|
345
|
+
)
|
|
346
|
+
} else if (strat === ScatterPointLabelStrategy.x) {
|
|
347
|
+
label = xColumn?.formatValue(
|
|
348
|
+
xColumn.valuesIncludingErrorValues[rowIndex]
|
|
349
|
+
)
|
|
350
|
+
} else {
|
|
351
|
+
label = timeColumn.formatTime(
|
|
352
|
+
timeColumn.valuesIncludingErrorValues[rowIndex] as number
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
return label
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
@computed private get allPointsBeforeEndpointsFilter(): SeriesPoint[] {
|
|
359
|
+
const { entityNameColumn, timeColumn } = this.transformedTable
|
|
360
|
+
const { xColumn, yColumn, sizeColumn, colorColumn } = this
|
|
361
|
+
|
|
362
|
+
// We are running this filter first because it only depends on author-specified config, not
|
|
363
|
+
// on any user interaction.
|
|
364
|
+
return this.removePointsOutsidePlane(
|
|
365
|
+
this.transformedTable.indices.map((rowIndex) => {
|
|
366
|
+
return {
|
|
367
|
+
x: xColumn.valuesIncludingErrorValues[rowIndex] as number,
|
|
368
|
+
y: yColumn.valuesIncludingErrorValues[rowIndex] as number,
|
|
369
|
+
size: defaultIfErrorValue(
|
|
370
|
+
sizeColumn.valuesIncludingErrorValues[rowIndex],
|
|
371
|
+
undefined
|
|
372
|
+
) as number | undefined,
|
|
373
|
+
color: defaultIfErrorValue(
|
|
374
|
+
colorColumn.valuesIncludingErrorValues[rowIndex],
|
|
375
|
+
undefined
|
|
376
|
+
) as string | number | undefined,
|
|
377
|
+
entityName: entityNameColumn.valuesIncludingErrorValues[
|
|
378
|
+
rowIndex
|
|
379
|
+
] as EntityName,
|
|
380
|
+
label: this.getPointLabel(rowIndex) ?? "",
|
|
381
|
+
timeValue: timeColumn.valuesIncludingErrorValues[
|
|
382
|
+
rowIndex
|
|
383
|
+
] as number,
|
|
384
|
+
time: {
|
|
385
|
+
x: xColumn.originalTimeColumn
|
|
386
|
+
.valuesIncludingErrorValues[rowIndex] as number,
|
|
387
|
+
y: yColumn.originalTimeColumn
|
|
388
|
+
.valuesIncludingErrorValues[rowIndex] as number,
|
|
389
|
+
// Technically, to be more correct, we should support distinct
|
|
390
|
+
// start and end times for each axis, but for simplicity we use
|
|
391
|
+
// a single span (see getAverageAnnualChangeIndicesByEntity)
|
|
392
|
+
span: this.manager.isRelativeMode
|
|
393
|
+
? [
|
|
394
|
+
yColumn.originalStartTimeColumn
|
|
395
|
+
.valuesIncludingErrorValues[
|
|
396
|
+
rowIndex
|
|
397
|
+
] as number,
|
|
398
|
+
yColumn.originalTimeColumn
|
|
399
|
+
.valuesIncludingErrorValues[
|
|
400
|
+
rowIndex
|
|
401
|
+
] as number,
|
|
402
|
+
]
|
|
403
|
+
: undefined,
|
|
404
|
+
},
|
|
405
|
+
}
|
|
406
|
+
})
|
|
407
|
+
)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
@computed get series(): ScatterSeries[] {
|
|
411
|
+
return Object.entries(
|
|
412
|
+
_.groupBy(this.allPointsBeforeEndpointsFilter, (p) => p.entityName)
|
|
413
|
+
).map(([entityName, points]) => {
|
|
414
|
+
const shortEntityName = getShortNameForEntity(entityName)
|
|
415
|
+
const series: ScatterSeries = {
|
|
416
|
+
seriesName: entityName,
|
|
417
|
+
label: shortEntityName ?? entityName,
|
|
418
|
+
color: SCATTER_POINT_DEFAULT_COLOR,
|
|
419
|
+
points,
|
|
420
|
+
focus: this.focusArray.state(entityName),
|
|
421
|
+
}
|
|
422
|
+
this.assignColorToSeries(entityName, series)
|
|
423
|
+
return series
|
|
424
|
+
})
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** Whether series are shown as lines (instead of single points) */
|
|
428
|
+
@computed get isConnected(): boolean {
|
|
429
|
+
return this.series.some((s) => s.points.length > 1)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
@computed get allPoints(): SeriesPoint[] {
|
|
433
|
+
return this.series.flatMap((series) => series.points)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
@computed private get selectedPoints(): SeriesPoint[] {
|
|
437
|
+
const seriesNamesSet = this.seriesNamesToHighlight
|
|
438
|
+
return this.allPoints.filter(
|
|
439
|
+
(point) => point.entityName && seriesNamesSet.has(point.entityName)
|
|
440
|
+
)
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
@computed get pointsForAxisDomains(): SeriesPoint[] {
|
|
444
|
+
if (
|
|
445
|
+
!this.selectionArray.numSelectedEntities ||
|
|
446
|
+
!this.manager.zoomToSelection
|
|
447
|
+
)
|
|
448
|
+
return this.allPoints
|
|
449
|
+
|
|
450
|
+
return this.selectedPoints.length ? this.selectedPoints : this.allPoints
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// domains across the entire timeline
|
|
454
|
+
private domainDefault(property: "x" | "y"): [number, number] {
|
|
455
|
+
const scaleType = property === "x" ? this.xScaleType : this.yScaleType
|
|
456
|
+
const defaultDomain: [number, number] =
|
|
457
|
+
scaleType === ScaleType.log ? [1, 100] : [-1, 1]
|
|
458
|
+
return (
|
|
459
|
+
domainExtent(
|
|
460
|
+
this.pointsForAxisDomains.map((point) => point[property]),
|
|
461
|
+
scaleType,
|
|
462
|
+
this.manager.zoomToSelection && this.selectedPoints.length
|
|
463
|
+
? 1.1
|
|
464
|
+
: 1
|
|
465
|
+
) ?? defaultDomain
|
|
466
|
+
)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
@computed get xDomainDefault(): [number, number] {
|
|
470
|
+
return this.domainDefault("x")
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
@computed get yDomainDefault(): [number, number] {
|
|
474
|
+
return this.domainDefault("y")
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
@computed get sizeDomain(): [number, number] {
|
|
478
|
+
if (this.sizeColumn.isMissing) return [1, 100]
|
|
479
|
+
if (
|
|
480
|
+
this.manager.isSingleTimeScatterAnimationActive &&
|
|
481
|
+
this.domainsForAnimation.size
|
|
482
|
+
) {
|
|
483
|
+
return this.domainsForAnimation.size
|
|
484
|
+
}
|
|
485
|
+
return computeSizeDomain(this.transformedTable, this.sizeColumn.slug)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
@computed get domainsForAnimation(): {
|
|
489
|
+
x?: ValueRange
|
|
490
|
+
y?: ValueRange
|
|
491
|
+
size?: ValueRange
|
|
492
|
+
} {
|
|
493
|
+
const { inputTable } = this
|
|
494
|
+
const { animationStartTime, animationEndTime } = this.manager
|
|
495
|
+
|
|
496
|
+
if (!animationStartTime || !animationEndTime) return {}
|
|
497
|
+
|
|
498
|
+
let table = inputTable.filterByTimeRange(
|
|
499
|
+
animationStartTime,
|
|
500
|
+
animationEndTime
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
if (this.manager.matchingEntitiesOnly && !this.colorColumn.isMissing) {
|
|
504
|
+
table = table.filterByEntityNames(
|
|
505
|
+
table.get(this.colorColumnSlug).uniqEntityNames
|
|
506
|
+
)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
table = table
|
|
510
|
+
.columnFilter(
|
|
511
|
+
this.xColumnSlug,
|
|
512
|
+
_.isNumber,
|
|
513
|
+
"Drop rows with non-number values in X column"
|
|
514
|
+
)
|
|
515
|
+
.columnFilter(
|
|
516
|
+
this.yColumnSlug,
|
|
517
|
+
_.isNumber,
|
|
518
|
+
"Drop rows with non-number values in Y column"
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
const xValues = table.get(this.xColumnSlug).uniqValues
|
|
522
|
+
const yValues = table.get(this.yColumnSlug).uniqValues
|
|
523
|
+
|
|
524
|
+
return {
|
|
525
|
+
x: domainExtent(xValues, this.xScaleType),
|
|
526
|
+
y: domainExtent(yValues, this.yScaleType),
|
|
527
|
+
size: computeSizeDomain(table, this.sizeColumn.slug),
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
@computed get validValuesForAxisDomainX(): number[] {
|
|
532
|
+
const { xScaleType, pointsForAxisDomains } = this
|
|
533
|
+
|
|
534
|
+
const values = pointsForAxisDomains.map((point) => point.x)
|
|
535
|
+
return xScaleType === ScaleType.log
|
|
536
|
+
? values.filter((v) => v > 0)
|
|
537
|
+
: values
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
@computed get validValuesForAxisDomainY(): number[] {
|
|
541
|
+
const { yScaleType, pointsForAxisDomains } = this
|
|
542
|
+
|
|
543
|
+
const values = pointsForAxisDomains.map((point) => point.y)
|
|
544
|
+
return yScaleType === ScaleType.log
|
|
545
|
+
? values.filter((v) => v > 0)
|
|
546
|
+
: values
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
@computed get verticalAxisLabel(): string {
|
|
550
|
+
const yAxisConfig = this.manager.yAxisConfig
|
|
551
|
+
|
|
552
|
+
let label = yAxisConfig?.label || this.yColumn?.displayName || ""
|
|
553
|
+
|
|
554
|
+
if (this.manager.isRelativeMode && label && label.length > 1) {
|
|
555
|
+
label = `Average annual change in ${lowerCaseFirstLetterUnlessAbbreviation(
|
|
556
|
+
label
|
|
557
|
+
)}`
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return label.trim()
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
@computed private get horizontalAxisLabelBase(): string {
|
|
564
|
+
const xDimName = this.xColumn?.displayName ?? ""
|
|
565
|
+
if (this.xOverrideTime !== undefined)
|
|
566
|
+
return `${xDimName} in ${this.xOverrideTime}`
|
|
567
|
+
return xDimName
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
@computed get horizontalAxisLabel(): string {
|
|
571
|
+
const xAxisConfig = this.manager.xAxisConfig
|
|
572
|
+
|
|
573
|
+
let label = xAxisConfig?.label || this.horizontalAxisLabelBase
|
|
574
|
+
if (this.manager.isRelativeMode && label && label.length > 1) {
|
|
575
|
+
label = `Average annual change in ${lowerCaseFirstLetterUnlessAbbreviation(
|
|
576
|
+
label
|
|
577
|
+
)}`
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return label.trim()
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
toHorizontalAxis(config: AxisConfig): HorizontalAxis {
|
|
584
|
+
const axis = config.toHorizontalAxis()
|
|
585
|
+
|
|
586
|
+
axis.formatColumn = this.xColumn
|
|
587
|
+
axis.scaleType = this.xScaleType
|
|
588
|
+
|
|
589
|
+
if (this.horizontalAxisLabel) axis.label = this.horizontalAxisLabel
|
|
590
|
+
|
|
591
|
+
if (
|
|
592
|
+
this.manager.isSingleTimeScatterAnimationActive &&
|
|
593
|
+
this.domainsForAnimation.x
|
|
594
|
+
) {
|
|
595
|
+
axis.updateDomainPreservingUserSettings(this.domainsForAnimation.x)
|
|
596
|
+
} else if (this.manager.isRelativeMode) {
|
|
597
|
+
axis.domain = this.xDomainDefault // Overwrite author's min/max
|
|
598
|
+
} else {
|
|
599
|
+
const isAnyValueOutsideUserDomain =
|
|
600
|
+
this.validValuesForAxisDomainX.some(
|
|
601
|
+
(value) => value < axis.domain[0] || value > axis.domain[1]
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
// only overwrite the authors's min/max if there is more than one unique value along the x-axis
|
|
605
|
+
// or if respecting the author's setting would hide data points
|
|
606
|
+
if (
|
|
607
|
+
new Set(this.validValuesForAxisDomainX).size > 1 ||
|
|
608
|
+
isAnyValueOutsideUserDomain
|
|
609
|
+
) {
|
|
610
|
+
axis.updateDomainPreservingUserSettings(this.xDomainDefault)
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return axis
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
toVerticalAxis(config: AxisConfig): VerticalAxis {
|
|
618
|
+
const axis = config.toVerticalAxis()
|
|
619
|
+
|
|
620
|
+
axis.formatColumn = this.yColumn
|
|
621
|
+
axis.scaleType = this.yScaleType
|
|
622
|
+
if (this.verticalAxisLabel) axis.label = this.verticalAxisLabel
|
|
623
|
+
|
|
624
|
+
if (
|
|
625
|
+
this.manager.isSingleTimeScatterAnimationActive &&
|
|
626
|
+
this.domainsForAnimation.y
|
|
627
|
+
) {
|
|
628
|
+
axis.updateDomainPreservingUserSettings(this.domainsForAnimation.y)
|
|
629
|
+
} else if (this.manager.isRelativeMode) {
|
|
630
|
+
axis.domain = this.yDomainDefault // Overwrite author's min/max
|
|
631
|
+
} else {
|
|
632
|
+
const isAnyValueOutsideUserDomain =
|
|
633
|
+
this.validValuesForAxisDomainY.some(
|
|
634
|
+
(value) => value < axis.domain[0] || value > axis.domain[1]
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
// only overwrite the authors's min/max if there is more than one unique value along the y-axis
|
|
638
|
+
// or if respecting the author's setting would hide data points
|
|
639
|
+
if (
|
|
640
|
+
new Set(this.validValuesForAxisDomainY).size > 1 ||
|
|
641
|
+
isAnyValueOutsideUserDomain
|
|
642
|
+
) {
|
|
643
|
+
axis.updateDomainPreservingUserSettings(this.yDomainDefault)
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
return axis
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
@computed get errorInfo(): ChartErrorInfo {
|
|
651
|
+
if (this.yColumn.isMissing) return { reason: "Missing Y axis variable" }
|
|
652
|
+
|
|
653
|
+
if (this.xColumn.isMissing) return { reason: "Missing X axis variable" }
|
|
654
|
+
|
|
655
|
+
const { entityTypePlural = "entities" } = this.manager
|
|
656
|
+
if (_.isEmpty(this.allEntityNamesWithXAndY)) {
|
|
657
|
+
if (
|
|
658
|
+
this.manager.isRelativeMode &&
|
|
659
|
+
this.manager.hasTimeline &&
|
|
660
|
+
this.manager.startTime === this.manager.endTime
|
|
661
|
+
) {
|
|
662
|
+
return {
|
|
663
|
+
reason: "Please select a start and end point on the timeline below",
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return {
|
|
667
|
+
reason: `No ${entityTypePlural} with data for both X and Y`,
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (_.isEmpty(this.series))
|
|
672
|
+
return {
|
|
673
|
+
reason: `No data for any ${entityTypePlural}`,
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return { reason: "" }
|
|
677
|
+
}
|
|
678
|
+
}
|