@publidata/utils-colors
Version:
Collection of methods to handle colors
155 lines (132 loc) • 5.12 kB
JavaScript
import { getCssVariableValue, isCssVariable } from "./helpers";
/**
*
* @param {array} markers - List of map markers (eg : [{color: "--var(verre)", id: 1234, title etc...}])
* @returns {object} - clusterProperties - MapLibre object to pass in mapSource
*/
export const generateClusterProperties = markers => {
const clusterProperties = {};
const usedColors = new Set();
const normalizedColor = color =>
isCssVariable(color) ? getCssVariableValue(color) : color;
const conditionsProperties = {};
const createCondition = color => {
conditionsProperties[color] = [
"any",
["in", color, ["get", "multiflowColors"]],
[
"all",
["==", ["get", "color"], color],
["==", ["get", "multiflowData"], null]
]
];
};
markers.forEach(marker => {
// Normalise la couleur principale du marker
const color = normalizedColor(marker.color);
const { multiflowColors } = marker;
if (Array.isArray(multiflowColors)) {
multiflowColors.forEach(multiflowColor => {
const normalizedMultiflowColor = normalizedColor(multiflowColor);
if (!usedColors.has(normalizedMultiflowColor)) {
createCondition(normalizedMultiflowColor);
usedColors.add(normalizedMultiflowColor);
}
});
}
if (!usedColors.has(color)) {
createCondition(color);
usedColors.add(color);
}
});
Object.entries(conditionsProperties).forEach(([color, condition]) => {
clusterProperties[color] = ["+", ["case", condition, 1, 0]];
});
return clusterProperties;
};
export const getClusterRadius = count =>
[
[1000, 50],
[100, 32],
[10, 24],
[0, 18]
].find(([threshold]) => count >= threshold)[1];
export const getClusterFontSize = count =>
[
[1000, 20],
[100, 18],
[10, 16],
[0, 14]
].find(([threshold]) => count >= threshold)[1];
/**
* @description - Generates a pie chart segment path using SVG path syntax.
* @param {number} start - Start angle in radians
* @param {number} end - End angle in radians
* @param {number} outerRadius - Outer radius of the pie chart
* @param {number} innerRadius - Inner radius of the pie chart
* @param {string} color - Color of the segment in pie chart
* @returns {string} - A path html string representing the pie chart segment
*/
export const getBorderTrunk = (start, end, outerRadius, innerRadius, color) => {
const angleToCoord = (angle, radius) => ({
x: radius * Math.cos(2 * Math.PI * (angle - 0.25)),
y: radius * Math.sin(2 * Math.PI * (angle - 0.25))
});
const adjustedEnd = end === 1 ? end - 0.00001 : end;
const largeArcFlag = adjustedEnd - start > 0.5 ? 1 : 0;
const startOuter = angleToCoord(start, outerRadius);
const endOuter = angleToCoord(adjustedEnd, outerRadius);
const startInner = angleToCoord(start, innerRadius);
const endInner = angleToCoord(adjustedEnd, innerRadius);
return `
<path d="
M ${outerRadius + startInner.x} ${outerRadius + startInner.y}
L ${outerRadius + startOuter.x} ${outerRadius + startOuter.y}
A ${outerRadius} ${outerRadius} 0 ${largeArcFlag} 1 ${
outerRadius + endOuter.x
} ${outerRadius + endOuter.y}
L ${outerRadius + endInner.x} ${outerRadius + endInner.y}
A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${
outerRadius + startInner.x
} ${outerRadius + startInner.y}
Z
" fill="${color}" />
`.trim();
};
export const generatePieChartIcons = properties => {
// Retrieve colors from properties and calculate counts
const { point_count: pointCount } = properties;
const colors = Object.keys(properties).filter(key => key.startsWith("#"));
const counts = colors.map(color => properties[color]);
const totalCount = counts.reduce((sum, count) => sum + count, 0);
// Define outer and inner radius based on total count
const outerRadius = getClusterRadius(pointCount || totalCount);
const innerRadius = Math.round(outerRadius * 0.65);
// Define color and font size
const fontSize = getClusterFontSize(pointCount || totalCount);
const width = outerRadius * 2;
// Generate arcs for the pie chart
const arcs = colors.map((color, index) => {
const start =
counts.slice(0, index).reduce((sum, count) => sum + count, 0) /
totalCount;
const end =
counts.slice(0, index + 1).reduce((sum, count) => sum + count, 0) /
totalCount;
return getBorderTrunk(start, end, outerRadius, innerRadius, color);
});
// Generate SVG HTML
let html = `
<div>
<svg width="${width}" height="${width}" viewBox="0 0 ${width} ${width}" text-anchor="middle" style="font: ${fontSize}px Lato, sans-serif; font-weight:600; display: block">`;
html += arcs.join("");
html += `<circle cx="${outerRadius}" cy="${outerRadius}" r="${innerRadius}" fill="white" />
<text dominant-baseline="central" transform="translate(${outerRadius}, ${outerRadius})">
${String(properties.point_count)}
</text>
</svg>
</div>`;
const element = document.createElement("div");
element.innerHTML = html.trim();
return element.firstChild;
};