Skip to content

Commit

Permalink
Fixed or dynamic number of columns for waffles
Browse files Browse the repository at this point in the history
  • Loading branch information
mthh committed Nov 7, 2024
1 parent 37e69e9 commit c3c6071
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 37 deletions.
27 changes: 19 additions & 8 deletions src/components/MapRenderer/WaffleMapRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {

// Helpers
import { bindDragBehavior, mergeFilterIds } from './common.tsx';
import { Mfloor, Mround } from '../../helpers/math';
import {
Mfloor, Mmin, Mround, Msqrt,
} from '../../helpers/math';

// Directives
import bindData from '../../directives/bind-data';
Expand Down Expand Up @@ -54,7 +56,6 @@ export default function waffleRenderer(
mgt:symbol-type={params.symbolType}
mgt:size={params.size}
mgt:anchor={params.anchor}
mgt:columns={params.columns}
mgt:spacing={params.spacing}
>
<For each={layerDescription.data.features}>
Expand All @@ -70,21 +71,30 @@ export default function waffleRenderer(
count: Mround(+feature.properties[variable.name] / params.symbolValue),
}));

const totalSymbols = variableSymbols.reduce((acc, variable) => acc + variable.count, 0);

// If the number of symbols is less than the number of columns,
// we use the number of symbols
// so the waffle is centered correctly around the few symbols
const columns = params.columns.type === 'fixed'
? Mmin(params.columns.value, totalSymbols)
: Mmin(Mround(Msqrt(totalSymbols)), totalSymbols);

// Central position of the symbol block
let offsetCentroidX = 0; // value for anchor = 'start'

if (params.symbolType === 'circle') {
if (params.anchor === 'middle') {
offsetCentroidX = (params.size + params.spacing)
* (params.columns / 2) - (params.size * 0.5);
* (columns / 2) - (params.size * 0.5);
} else if (params.anchor === 'end') {
offsetCentroidX = (params.size + params.spacing) * params.columns - params.size;
offsetCentroidX = (params.size + params.spacing) * columns - params.size;
}
} else { // eslint-disable-next-line no-lonely-if
if (params.anchor === 'middle') {
offsetCentroidX = (params.size + params.spacing) * (params.columns / 2);
offsetCentroidX = (params.size + params.spacing) * (columns / 2);
} else if (params.anchor === 'end') {
offsetCentroidX = (params.size + params.spacing) * params.columns;
offsetCentroidX = (params.size + params.spacing) * columns;
}
}

Expand All @@ -93,15 +103,16 @@ export default function waffleRenderer(
return <g
// @ts-expect-error because use:bind-data isn't a property of this element
use:bindData={feature}
mgt:columns={columns}
>
<For each={variableSymbols}>
{(variable) => <For each={Array.from({ length: variable.count })}>
{(_, i) => {
const tx = Mround(
(symbolIndex % params.columns) * (params.size + params.spacing),
(symbolIndex % columns) * (params.size + params.spacing),
);
const ty = Mfloor(
Mfloor(symbolIndex / params.columns) * (params.size + params.spacing),
Mfloor(symbolIndex / columns) * (params.size + params.spacing),
);
symbolIndex += 1; // Increment for next position
return params.symbolType === 'circle' ? (
Expand Down
28 changes: 15 additions & 13 deletions src/components/Modals/LayerSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,19 +231,21 @@ function makeSettingsWaffle(
step={1}
bindKeyUpAsChange={true}
/>
<InputFieldNumber
label={LL().FunctionalitiesSection.WaffleOptions.Columns()}
value={props.rendererParameters.columns!}
onChange={(v) => {
updateProp(props.id, ['rendererParameters', 'columns'], v);
redrawLayer(props.id);
}}
min={1}
strictMin={true}
max={20}
step={1}
bindKeyUpAsChange={true}
/>
<Show when={props.rendererParameters.columns.type === 'fixed'}>
<InputFieldNumber
label={LL().FunctionalitiesSection.WaffleOptions.ColumnsNumber()}
value={props.rendererParameters.columns.value!}
onChange={(v) => {
updateProp(props.id, ['rendererParameters', 'columns', 'value'], v);
redrawLayer(props.id);
}}
min={1}
strictMin={true}
max={20}
step={1}
bindKeyUpAsChange={true}
/>
</Show>
<InputFieldNumber
label={LL().FunctionalitiesSection.WaffleOptions.Spacing()}
value={props.rendererParameters.spacing!}
Expand Down
47 changes: 37 additions & 10 deletions src/components/PortrayalOption/WaffleSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ function guessSymbolValue(
minSum = Mmin(minSum, sum);
});

if (maxSum < 45) {
return 1;
}

// We want to find a divisor (ideally a multiple of 10) that
// allows the user to encode the sum of the selected variables
// in a stack of symbols (without having too much symbols for the
Expand Down Expand Up @@ -137,7 +141,7 @@ function onClickValidate(
symbolValue: number,
symbolValueSentence: string,
size: number,
columns: number,
columns: { type: 'dynamic' } | { type: 'fixed', value: number },
spacing: number,
symbolType: ProportionalSymbolsSymbolType.circle | ProportionalSymbolsSymbolType.square,
newLayerName: string,
Expand Down Expand Up @@ -302,6 +306,11 @@ export default function WaffleSettings(
setSymbolSize,
] = createSignal<number>(10);

const [
columnsType,
setColumnsType,
] = createSignal<'dynamic' | 'fixed'>('fixed');

// Number of columns
const [
columns,
Expand Down Expand Up @@ -349,6 +358,10 @@ export default function WaffleSettings(

await yieldOrContinue('smooth');

const ct = columnsType() === 'fixed'
? { type: 'fixed', value: columns() }
: { type: 'dynamic' };

// Actually create the portrayal
setTimeout(() => {
onClickValidate(
Expand All @@ -357,7 +370,7 @@ export default function WaffleSettings(
symbolValue(),
LL().FunctionalitiesSection.WaffleOptions.SymbolRatioNote({ value: symbolValue() }),
symbolSize(),
columns(),
ct,
space(),
symbolType(),
layerName,
Expand Down Expand Up @@ -423,14 +436,28 @@ export default function WaffleSettings(
max={30}
step={1}
/>
<InputFieldNumber
label={LL().FunctionalitiesSection.WaffleOptions.Columns()}
value={columns()}
onChange={(value) => { setColumns(value); }}
min={1}
max={30}
step={1}
/>
<InputFieldSelect
label={LL().FunctionalitiesSection.WaffleOptions.ColumnsType()}
value={columnsType()}
onChange={(value) => { setColumnsType(value as 'dynamic' | 'fixed'); }}
>
<option value={'fixed'}>
{LL().FunctionalitiesSection.WaffleOptions.ColumnsFixed()}
</option>
<option value={'dynamic'}>
{LL().FunctionalitiesSection.WaffleOptions.ColumnsDynamic()}
</option>
</InputFieldSelect>
<Show when={columnsType() === 'fixed'}>
<InputFieldNumber
label={LL().FunctionalitiesSection.WaffleOptions.ColumnsNumber()}
value={columns()}
onChange={(value) => { setColumns(value); }}
min={1}
max={30}
step={1}
/>
</Show>
<InputFieldNumber
label={LL().FunctionalitiesSection.WaffleOptions.SymbolSize()}
value={symbolSize()}
Expand Down
2 changes: 1 addition & 1 deletion src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ interface WaffleParameters {
// The size for each symbol
size: number,
// The number of columns for the waffle
columns: number,
columns: { type: 'dynamic' } | { type: 'fixed', value: number },
// How much stock is represented by each symbol
symbolValue: number,
// How much space between each symbol
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,11 @@ export const redrawPaths = (svgElement: SVGSVGElement & IZoomable) => {
} else if (typePortrayal === 'waffle') {
const symbolType = g.getAttribute('mgt:symbol-type')!;
const size = +g.getAttribute('mgt:size')!;
const columns = +g.getAttribute('mgt:columns')!;
const spacing = +g.getAttribute('mgt:spacing')!;
const anchor = g.getAttribute('mgt:anchor')!;

g.querySelectorAll('g').forEach((gg) => {
const columns = +gg.getAttribute('mgt:columns')!;
const coords = globalStore.pathGenerator.centroid(
// eslint-disable-next-line no-underscore-dangle
(gg as SVGGElement & ID3Element).__data__.geometry,
Expand Down
5 changes: 4 additions & 1 deletion src/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1100,7 +1100,10 @@ const en = {
Variables: 'Variables (two or more)',
SymbolType: 'Symbol type',
Spacing: 'Spacing',
Columns: 'Number of columns',
ColumnsType: 'Columns type',
ColumnsDynamic: 'Dynamic',
ColumnsFixed: 'Fixed',
ColumnsNumber: 'Number of columns',
SymbolSize: 'Symbol size',
SymbolValue: 'Value expressed by each symbol',
NewLayerName: 'Waffle_{layerName}',
Expand Down
5 changes: 4 additions & 1 deletion src/i18n/fr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1100,7 +1100,10 @@ const fr = {
Variables: 'Variables (deux ou plus)',
SymbolType: 'Type de symbole',
Spacing: 'Espacement',
Columns: 'Nombre de colonnes',
ColumnsType: 'Type de colonnes',
ColumnsDynamic: 'Dynamique',
ColumnsFixed: 'Fixe',
ColumnsNumber: 'Nombre de colonnes',
SymbolSize: 'Taille du symbole',
SymbolValue: 'Valeur représentée par chaque symbole',
NewLayerName: 'Gaufre_{layerName}',
Expand Down
28 changes: 26 additions & 2 deletions src/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4419,10 +4419,22 @@ type RootTranslation = {
* S​p​a​c​i​n​g
*/
Spacing: string
/**
* C​o​l​u​m​n​s​ ​t​y​p​e
*/
ColumnsType: string
/**
* D​y​n​a​m​i​c
*/
ColumnsDynamic: string
/**
* F​i​x​e​d
*/
ColumnsFixed: string
/**
* N​u​m​b​e​r​ ​o​f​ ​c​o​l​u​m​n​s
*/
Columns: string
ColumnsNumber: string
/**
* S​y​m​b​o​l​ ​s​i​z​e
*/
Expand Down Expand Up @@ -10291,10 +10303,22 @@ export type TranslationFunctions = {
* Spacing
*/
Spacing: () => LocalizedString
/**
* Columns type
*/
ColumnsType: () => LocalizedString
/**
* Dynamic
*/
ColumnsDynamic: () => LocalizedString
/**
* Fixed
*/
ColumnsFixed: () => LocalizedString
/**
* Number of columns
*/
Columns: () => LocalizedString
ColumnsNumber: () => LocalizedString
/**
* Symbol size
*/
Expand Down

0 comments on commit c3c6071

Please sign in to comment.