Skip to content

Commit

Permalink
Add option for vertical centering of waffle group
Browse files Browse the repository at this point in the history
  • Loading branch information
mthh committed Nov 13, 2024
1 parent 3861955 commit e0080d6
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 23 deletions.
41 changes: 32 additions & 9 deletions src/components/MapRenderer/WaffleMapRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ export default function waffleRenderer(
mgt:portrayal-type={layerDescription.representationType}
mgt:symbol-type={params.symbolType}
mgt:size={params.size}
mgt:anchor={params.anchor}
mgt:horizontalAnchor={params.horizontalAnchor}
mgt:verticalAnchor={params.verticalAnchor}
mgt:spacing={params.spacing}
>
<For each={layerDescription.data.features}>
Expand All @@ -80,34 +81,56 @@ export default function waffleRenderer(
? Mmin(params.columns.value, totalSymbols)
: Mmin(Mround(Msqrt(totalSymbols)), totalSymbols);

// Central position of the symbol block
// Central position of the symbol block (on the x-axis)
let offsetCentroidX = 0; // value for anchor = 'start'

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

// Central position of the symbol block (on the y-axis)
let offsetCentroidY = 0; // value for verticalAnchor = 'bottom'

if (params.symbolType === 'circle') {
if (params.verticalAnchor === 'middle') {
offsetCentroidY = (params.size + params.spacing) * (Mfloor(
totalSymbols / columns,
) / 2);
} else if (params.verticalAnchor === 'top') {
offsetCentroidY = (params.size + params.spacing) * Mfloor(totalSymbols / columns);
}
} else { // eslint-disable-next-line no-lonely-if
if (params.verticalAnchor === 'middle') {
offsetCentroidY = (params.size + params.spacing) * (Mfloor(
totalSymbols / columns,
) / 2);
} else if (params.verticalAnchor === 'top') {
offsetCentroidY = (params.size + params.spacing) * Mfloor(totalSymbols / columns);
}
}

let symbolIndex = 0; // Counter for symbol position

return <g
// @ts-expect-error because use:bind-data isn't a property of this element
use:bindData={feature}
mgt:columns={columns}
mgt:totalSymbols={totalSymbols}
>
<For each={variableSymbols}>
{(variable) => <For each={Array.from({ length: variable.count })}>
{(_, i) => {
{() => {
const tx = Mround(
(symbolIndex % columns) * (params.size + params.spacing),
);
Expand All @@ -118,14 +141,14 @@ export default function waffleRenderer(
return params.symbolType === 'circle' ? (
<circle
cx={projectedCoords()[0] - offsetCentroidX + tx}
cy={projectedCoords()[1] - (params.size / 2) - ty}
cy={projectedCoords()[1] + offsetCentroidY - (params.size / 2) - ty}
r={params.size / 2}
fill={variable.color}
/>
) : (
<rect
x={projectedCoords()[0] - offsetCentroidX + tx}
y={projectedCoords()[1] - params.size - ty}
y={projectedCoords()[1] + offsetCentroidY - params.size - ty}
width={params.size}
height={params.size}
fill={variable.color}
Expand Down
18 changes: 15 additions & 3 deletions src/components/Modals/LayerSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -272,17 +272,29 @@ function makeSettingsWaffle(
step={10}
/>
<InputFieldSelect
label={LL().LayerSettings.SymbolAnchor()}
label={LL().LayerSettings.SymbolHorizontalAnchor()}
onChange={(v) => {
updateProp(props.id, ['rendererParameters', 'anchor'], v);
updateProp(props.id, ['rendererParameters', 'horizontalAnchor'], v);
redrawLayer(props.id);
}}
value={props.rendererParameters.anchor}
value={props.rendererParameters.horizontalAnchor}
>
<option value="start">{LL().LayerSettings.TextAnchorStart()}</option>
<option value="middle">{LL().LayerSettings.TextAnchorMiddle()}</option>
<option value="end">{LL().LayerSettings.TextAnchorEnd()}</option>
</InputFieldSelect>
<InputFieldSelect
label={LL().LayerSettings.SymbolVerticalAnchor()}
onChange={(v) => {
updateProp(props.id, ['rendererParameters', 'verticalAnchor'], v);
redrawLayer(props.id);
}}
value={props.rendererParameters.verticalAnchor}
>
<option value="top">{LL().LayerSettings.TextAnchorTop()}</option>
<option value="middle">{LL().LayerSettings.TextAnchorMiddle()}</option>
<option value="bottom">{LL().LayerSettings.TextAnchorBottom()}</option>
</InputFieldSelect>
<InputFieldWidthColorOpacity
label={LL().LayerSettings.Stroke()}
valueWidth={props.strokeWidth!}
Expand Down
5 changes: 3 additions & 2 deletions src/components/PortrayalOption/WaffleSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import InputFieldNumber from '../Inputs/InputNumber.tsx';
import InputFieldSelect from '../Inputs/InputSelect.tsx';
import InputResultName from './InputResultName.tsx';
import { openLayerManager } from '../LeftMenu/LeftMenu.tsx';
import MessageBlock from '../MessageBlock.tsx';

// Stores
import { applicationSettingsStore } from '../../store/ApplicationSettingsStore';
Expand All @@ -53,7 +54,6 @@ import {
type WaffleParameters,
} from '../../global.d';
import type { PortrayalSettingsProps } from './common';
import MessageBlock from '../MessageBlock.tsx';

function guessSymbolValue(
layerId: string,
Expand Down Expand Up @@ -194,7 +194,8 @@ function onClickValidate(
columns,
symbolValue,
spacing,
anchor: 'middle',
horizontalAnchor: 'middle',
verticalAnchor: (columns.type === 'dynamic') ? 'middle' : 'bottom',
movable: false,
};

Expand Down
4 changes: 3 additions & 1 deletion src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,9 @@ interface WaffleParameters {
// The type of symbol used to represent the waffles
symbolType: ProportionalSymbolsSymbolType.circle | ProportionalSymbolsSymbolType.square,
// Horizontal anchor of the waffle group
anchor: 'start' | 'middle' | 'end',
horizontalAnchor: 'start' | 'middle' | 'end',
// Vertical anchor of the waffle group
verticalAnchor: 'top' | 'middle' | 'bottom',
// The size for each symbol
size: number,
// The number of columns for the waffle
Expand Down
34 changes: 26 additions & 8 deletions src/helpers/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,29 +344,47 @@ export const redrawPaths = (svgElement: SVGSVGElement & IZoomable) => {
const symbolType = g.getAttribute('mgt:symbol-type')!;
const size = +g.getAttribute('mgt:size')!;
const spacing = +g.getAttribute('mgt:spacing')!;
const anchor = g.getAttribute('mgt:anchor')!;
const horizontalAnchor = g.getAttribute('mgt:horizontalAnchor')!;
const verticalAnchor = g.getAttribute('mgt:verticalAnchor')!;

g.querySelectorAll('g').forEach((gg) => {
const columns = +gg.getAttribute('mgt:columns')!;
const totalSymbols = +gg.getAttribute('mgt:totalSymbols')!;
const coords = globalStore.pathGenerator.centroid(
// eslint-disable-next-line no-underscore-dangle
(gg as SVGGElement & ID3Element).__data__.geometry,
);
if (!Number.isNaN(coords[0])) {
let offsetCentroidX = 0; // value for anchor = 'start'
let offsetCentroidX = 0; // value for horizontalAnchor = 'start'
if (symbolType === 'circle') {
if (anchor === 'middle') {
if (horizontalAnchor === 'middle') {
offsetCentroidX = (size + spacing) * (columns / 2) - (size * 0.5);
} else if (anchor === 'end') {
} else if (horizontalAnchor === 'end') {
offsetCentroidX = (size + spacing) * columns - size;
}
} else { // eslint-disable-next-line no-lonely-if
if (anchor === 'middle') {
if (horizontalAnchor === 'middle') {
offsetCentroidX = (size + spacing) * (columns / 2);
} else if (anchor === 'end') {
} else if (horizontalAnchor === 'end') {
offsetCentroidX = (size + spacing) * columns;
}
}
let offsetCentroidY = 0; // value for verticalAnchor = 'bottom'
if (symbolType === 'circle') {
if (verticalAnchor === 'middle') {
offsetCentroidY = (size + spacing) * (Mfloor(
totalSymbols / columns,
) / 2);
} else if (verticalAnchor === 'top') {
offsetCentroidY = (size + spacing) * Mfloor(totalSymbols / columns);
}
} else { // eslint-disable-next-line no-lonely-if
if (verticalAnchor === 'middle') {
offsetCentroidY = (size + spacing) * (Mfloor(totalSymbols / columns) / 2);
} else if (verticalAnchor === 'top') {
offsetCentroidY = (size + spacing) * Mfloor(totalSymbols / columns);
}
}
let symbolIndex = 0;
if (symbolType === 'circle') {
gg.querySelectorAll('circle').forEach((c) => {
Expand All @@ -378,7 +396,7 @@ export const redrawPaths = (svgElement: SVGSVGElement & IZoomable) => {
);
symbolIndex += 1;
c.setAttribute('cx', `${coords[0] - offsetCentroidX + tx}`);
c.setAttribute('cy', `${coords[1] - (size / 2) - ty}`);
c.setAttribute('cy', `${coords[1] + offsetCentroidY - (size / 2) - ty}`);
});
} else { // symbolType === 'square'
gg.querySelectorAll('rect').forEach((r) => {
Expand All @@ -389,7 +407,7 @@ export const redrawPaths = (svgElement: SVGSVGElement & IZoomable) => {
Mfloor(symbolIndex / columns) * (size + spacing),
);
r.setAttribute('x', `${coords[0] - offsetCentroidX + tx}`);
r.setAttribute('y', `${coords[1] - size - ty}`);
r.setAttribute('y', `${coords[1] + offsetCentroidY - size - ty}`);
symbolIndex += 1;
});
}
Expand Down
4 changes: 4 additions & 0 deletions src/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1192,10 +1192,14 @@ const en = {
XOffset: 'X offset',
YOffset: 'Y offset',
SymbolAnchor: 'Symbol anchor',
SymbolHorizontalAnchor: 'Ancrage horizontal du symbole',
SymbolVerticalAnchor: 'Ancrage vertical du symbole',
TextAnchor: 'Text anchor',
TextAnchorStart: 'Start',
TextAnchorMiddle: 'Middle',
TextAnchorEnd: 'End',
TextAnchorTop: 'Top',
TextAnchorBottom: 'Bottom',
FontStyle: 'Font style',
FontWeight: 'Font weight',
BufferAroundText: 'Buffer around text',
Expand Down
4 changes: 4 additions & 0 deletions src/i18n/fr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1192,10 +1192,14 @@ const fr = {
XOffset: 'Décalage en X',
YOffset: 'Décalage en Y',
SymbolAnchor: 'Ancrage du symbole',
SymbolHorizontalAnchor: 'Ancrage horizontal du symbole',
SymbolVerticalAnchor: 'Ancrage vertical du symbole',
TextAnchor: 'Ancrage du texte',
TextAnchorStart: 'Début',
TextAnchorMiddle: 'Milieu',
TextAnchorEnd: 'Fin',
TextAnchorTop: 'Haut',
TextAnchorBottom: 'Bas',
FontStyle: 'Style de police',
FontWeight: 'Épaisseur de police',
BufferAroundText: 'Tampon autour du texte',
Expand Down
32 changes: 32 additions & 0 deletions src/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4764,6 +4764,14 @@ type RootTranslation = {
* S​y​m​b​o​l​ ​a​n​c​h​o​r
*/
SymbolAnchor: string
/**
* A​n​c​r​a​g​e​ ​h​o​r​i​z​o​n​t​a​l​ ​d​u​ ​s​y​m​b​o​l​e
*/
SymbolHorizontalAnchor: string
/**
* A​n​c​r​a​g​e​ ​v​e​r​t​i​c​a​l​ ​d​u​ ​s​y​m​b​o​l​e
*/
SymbolVerticalAnchor: string
/**
* T​e​x​t​ ​a​n​c​h​o​r
*/
Expand All @@ -4780,6 +4788,14 @@ type RootTranslation = {
* E​n​d
*/
TextAnchorEnd: string
/**
* T​o​p
*/
TextAnchorTop: string
/**
* B​o​t​t​o​m
*/
TextAnchorBottom: string
/**
* F​o​n​t​ ​s​t​y​l​e
*/
Expand Down Expand Up @@ -10644,6 +10660,14 @@ export type TranslationFunctions = {
* Symbol anchor
*/
SymbolAnchor: () => LocalizedString
/**
* Ancrage horizontal du symbole
*/
SymbolHorizontalAnchor: () => LocalizedString
/**
* Ancrage vertical du symbole
*/
SymbolVerticalAnchor: () => LocalizedString
/**
* Text anchor
*/
Expand All @@ -10660,6 +10684,14 @@ export type TranslationFunctions = {
* End
*/
TextAnchorEnd: () => LocalizedString
/**
* Top
*/
TextAnchorTop: () => LocalizedString
/**
* Bottom
*/
TextAnchorBottom: () => LocalizedString
/**
* Font style
*/
Expand Down

0 comments on commit e0080d6

Please sign in to comment.