Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: New design for color scale palette picking for maps #294

Merged
merged 21 commits into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d3ce6fb
refactor: Improve map color scale types
bprusinowski Jan 25, 2022
b86b22b
feat: Add radio + select elements for color scale types for maps
bprusinowski Jan 25, 2022
aaa0d09
feat: Add types for color palettes for maps
bprusinowski Jan 26, 2022
5078e34
refactor: Use proper color palette naming conventions
bprusinowski Jan 26, 2022
afbbc9a
feat: Add ColorRampField
bprusinowski Jan 26, 2022
8ddf7d1
chore: Remove redundant ColorRamp
bprusinowski Jan 26, 2022
fa066c8
feat: Use ColorRampField in map chart options
bprusinowski Jan 26, 2022
c15b844
feat: Add translations for divergent and sequential color palettes
bprusinowski Jan 26, 2022
1cdae16
feat: Add color types to tables
bprusinowski Jan 26, 2022
c338eee
fix: Set correct nbClass type to chart state
bprusinowski Jan 26, 2022
48d1724
feat: Do not cut color domain range on nbClass change
bprusinowski Jan 26, 2022
20d3349
fix: Retrieve number of geoShapes only if geoDimension if here
bprusinowski Jan 27, 2022
05e1824
feat: Add disabled prop to ColorRamp
bprusinowski Jan 27, 2022
36627fa
feat: Use translations for discrete color scale names
bprusinowski Jan 27, 2022
64809ca
refactor: Add more types for colors
bprusinowski Jan 27, 2022
8082a27
refactor: Move default palette in ColorRamp to useMemo
bprusinowski Jan 27, 2022
832090d
refactor: Small improvements to ColorRampField
bprusinowski Jan 27, 2022
108b56c
feat: Add ColorRamp docs
bprusinowski Jan 27, 2022
c419d6e
refactor: Move selectColorPicker style to theme object
bprusinowski Jan 27, 2022
b070e07
feat: Add types to field and path in ColorRampField
bprusinowski Jan 27, 2022
c60d46e
Merge branch 'main' of github.com:visualize-admin/visualization-tool …
bprusinowski Jan 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/charts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,10 @@ export const getInitialConfig = ({
componentIri: geoShapes[0]?.iri || "",
measureIri: measures[0].iri,
hierarchyLevel: 1,
colorScaleType: "continuous",
colorScaleInterpolationType: "linear",
palette: "oranges",
nbClass: 5,
paletteType: "continuous",
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved
},
symbolLayer: {
show: geoShapes.length === 0,
Expand Down
52 changes: 14 additions & 38 deletions app/charts/map/map-legend.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
axisBottom,
interpolateOranges,
NumberValue,
range,
ScaleLinear,
Expand All @@ -14,6 +13,7 @@ import {
import * as React from "react";
import { useEffect, useMemo, useRef } from "react";
import { Box, Flex, Text } from "theme-ui";
import { ColorRamp } from "../../configurator/components/chart-controls/color-ramp";
import {
getColorInterpolator,
useFormatInteger,
Expand Down Expand Up @@ -92,10 +92,18 @@ export const MapLegend = () => {
{areaLayer.measureLabel}
</Text>
)}
{areaLayer.paletteType === "continuous" && <ContinuousColorLegend />}
{areaLayer.paletteType === "discrete" && <QuantizeColorLegend />}
{areaLayer.paletteType === "quantile" && <QuantileColorLegend />}
{areaLayer.paletteType === "jenks" && <JenksColorLegend />}
{areaLayer.colorScaleInterpolationType === "linear" && (
<ContinuousColorLegend />
)}
{areaLayer.colorScaleInterpolationType === "quantize" && (
<QuantizeColorLegend />
)}
{areaLayer.colorScaleInterpolationType === "quantile" && (
<QuantileColorLegend />
)}
{areaLayer.colorScaleInterpolationType === "jenks" && (
<JenksColorLegend />
)}
</Box>
)}

Expand Down Expand Up @@ -509,7 +517,7 @@ const ContinuousColorLegend = () => {
width={width - MARGIN.left - MARGIN.right}
height={COLOR_RAMP_HEIGHT}
colorInterpolator={getColorInterpolator(palette)}
nbClass={width}
nbClass={width - MARGIN.left - MARGIN.right}
/>
</foreignObject>
<g
Expand Down Expand Up @@ -558,35 +566,3 @@ const DataPointIndicator = ({
</>
);
};

const ColorRamp = ({
colorInterpolator = interpolateOranges,
nbClass,
width,
height,
}: {
colorInterpolator: (t: number) => string;
nbClass: number;
width: number;
height: number;
}) => {
const canvasRef = useRef<HTMLCanvasElement>(null);

useEffect(() => {
const canvas = canvasRef.current;
const context = canvas && canvas.getContext("2d");

if (canvas && context) {
context.clearRect(0, 0, width, height);
canvas.style.imageRendering = "-moz-crisp-edges";
canvas.style.imageRendering = "pixelated";

for (let i = 0; i < nbClass; ++i) {
context.fillStyle = colorInterpolator(i / (nbClass - 1));
context.fillRect(i, 0, 1, height);
}
}
});

return <canvas ref={canvasRef} width={width} height={height} />;
};
56 changes: 32 additions & 24 deletions app/charts/map/map-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ import {
} from "../../configurator/components/ui-helpers";
import {
BaseLayer,
ColorScaleInterpolationType,
DivergingPaletteType,
MapFields,
PaletteType,
SequentialPaletteType,
} from "../../configurator/config-types";
import {
GeoData,
Expand Down Expand Up @@ -61,8 +63,8 @@ export interface MapState {
| ScaleQuantile<string>
| ScaleLinear<string, string>
| ScaleThreshold<number, string>;
paletteType: PaletteType;
palette: string;
colorScaleInterpolationType: ColorScaleInterpolationType;
palette: DivergingPaletteType | SequentialPaletteType;
nbClass: number;
dataDomain: [number, number];
};
Expand All @@ -80,36 +82,38 @@ export interface MapState {
}

const getColorScale = ({
paletteType,
scaleInterpolationType,
palette,
getValue,
data,
dataDomain,
nbClass,
}: {
paletteType: PaletteType;
palette: string;
scaleInterpolationType: ColorScaleInterpolationType;
palette: DivergingPaletteType | SequentialPaletteType;
getValue: (x: Observation) => number | null;
data: Observation[];
dataDomain: [number, number];
nbClass: number;
}) => {
const paletteDomain = getSingleHueSequentialPalette({
palette,
nbClass: 9,
});
const interpolator = getColorInterpolator(palette);
const getDiscreteRange = () => {
return Array.from({ length: nbClass }, (_, i) =>
interpolator(i / (nbClass - 1))
);
};
bprusinowski marked this conversation as resolved.
Show resolved Hide resolved

switch (paletteType) {
case "continuous":
return scaleSequential(getColorInterpolator(palette)).domain(dataDomain);
case "discrete":
switch (scaleInterpolationType) {
case "linear":
return scaleSequential(interpolator).domain(dataDomain);
case "quantize":
return scaleQuantize<string>()
.domain(dataDomain)
.range(getSingleHueSequentialPalette({ palette, nbClass }));
.range(getDiscreteRange());
case "quantile":
return scaleQuantile<string>()
.domain(data.map((d) => getValue(d)))
.range(getSingleHueSequentialPalette({ palette, nbClass }));
.range(getDiscreteRange());
case "jenks":
const ckMeansThresholds = ckmeans(
data.map((d) => getValue(d) ?? NaN),
Expand All @@ -118,8 +122,13 @@ const getColorScale = ({

return scaleThreshold<number, string>()
.domain(ckMeansThresholds)
.range(getSingleHueSequentialPalette({ palette, nbClass }));
.range(getDiscreteRange());
default:
const paletteDomain = getSingleHueSequentialPalette({
palette,
nbClass: 9,
});

return scaleLinear<string>()
.domain(dataDomain)
.range([paletteDomain[0], paletteDomain[paletteDomain.length - 1]]);
Expand All @@ -140,7 +149,6 @@ const useMapState = ({
}): MapState => {
const width = useWidth();
const { areaLayer, symbolLayer } = fields;
const { palette, nbClass, paletteType } = areaLayer;

const getAreaLabel = useStringVariable(areaLayer.componentIri);
const getSymbolLabel = useStringVariable(symbolLayer.componentIri);
Expand Down Expand Up @@ -227,12 +235,12 @@ const useMapState = ({
]) as [number, number];

const areaColorScale = getColorScale({
paletteType,
palette,
scaleInterpolationType: areaLayer.colorScaleInterpolationType,
palette: areaLayer.palette,
getValue: getAreaValue,
data: areaData,
dataDomain: areaDataDomain,
nbClass,
nbClass: areaLayer.nbClass,
});

const getAreaColor = (v: number | null) => {
Expand Down Expand Up @@ -279,9 +287,9 @@ const useMapState = ({
getValue: getAreaValue,
getColor: getAreaColor,
colorScale: areaColorScale,
paletteType,
palette,
nbClass: nbClass,
colorScaleInterpolationType: areaLayer.colorScaleInterpolationType,
palette: areaLayer.palette,
nbClass: areaLayer.nbClass,
dataDomain: areaDataDomain,
},
symbolLayer: {
Expand Down
36 changes: 8 additions & 28 deletions app/configurator/components/chart-controls/color-palette.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Trans } from "@lingui/macro";
import { Box, Button, Flex, Text } from "theme-ui";
import { useSelect } from "downshift";
import get from "lodash/get";
import { useCallback } from "react";
import { Box, Button, Flex, Text } from "theme-ui";
import { ConfiguratorStateConfiguringChart, useConfiguratorState } from "../..";
import { Label } from "../../../components/form";
import { DimensionMetaDataFragment } from "../../../graphql/query-hooks";
import { Icon } from "../../../icons";
import {
categoricalPalettes,
divergingSteppedPalettes,
getPalette,
mapColorsToComponentValuesIris,
sequentialPalettes,
} from "../ui-helpers";
import { DimensionMetaDataFragment } from "../../../graphql/query-hooks";
import { Icon } from "../../../icons";

type Props = {
field: string;
Expand All @@ -31,7 +31,7 @@ export const ColorPalette = ({

const palettes =
component?.__typename === "Measure"
? sequentialPalettes
? divergingSteppedPalettes
: categoricalPalettes;

const currentPaletteName = get(
Expand Down Expand Up @@ -73,34 +73,14 @@ export const ColorPalette = ({
});

return (
<Box mt={2} sx={{ pointerEvents: disabled ? "none" : "unset" }}>
<Box mt={2} sx={{ pointerEvents: disabled ? "none" : "auto" }}>
<Label disabled={disabled} smaller {...getLabelProps()}>
<Trans id="controls.color.palette">Color Palette</Trans>
</Label>
<Button
variant="selectColorPicker"
{...getToggleButtonProps()}
sx={{
color: "monochrome700",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
bg: "monochrome100",
p: 1,
height: "40px",
borderWidth: "1px",
borderStyle: "solid",
borderColor: "monochrome500",
":hover": {
bg: "monochrome100",
},
":active": {
bg: "monochrome100",
},
":disabled": {
cursor: "initial",
bg: "muted",
},
}}
sx={{ cursor: "pointer" }}
>
{state.state === "CONFIGURING_CHART" && (
<Flex>
Expand Down
Loading