Skip to content

Commit

Permalink
fix zone y axis max + breakpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
braddf committed Nov 28, 2024
1 parent 300dac1 commit 361b701
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 9 deletions.
20 changes: 14 additions & 6 deletions apps/nowcasting-app/components/charts/gsp-pv-remix-chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@ import Spinner from "../../icons/spinner";
import { ForecastValue } from "../../types";
import React, { FC } from "react";
import { NationalAggregation } from "../../map/types";
import { getTicks } from "../../helpers/chartUtils";

// We want to have the ymax of the graph to be related to the capacity of the GspPvRemixChart
// Static constant below of this function so we don't call dynamically unnecessarily.
// import { generateYMaxTickArray } from "../../helpers/chartUtils";
// console.log("Y_MAX_TICKS", generateYMaxTickArray());
//
// We want to have the yMax of the graph to be related to the capacity of the GspPvRemixChart.
// If we use the raw values, the graph looks funny, i.e y major ticks are 0 100 232
// So, we round these up to the following numbers
const yMax_levels = [
3, 9, 20, 28, 36, 45, 60, 80, 100, 120, 160, 200, 240, 300, 320, 360, 400, 450, 600
// So, we round these up to the following numbers, which hopefully split nicely into the y-axis.
// Uncomment the above function to get updated values should we need to change these
const Y_MAX_TICKS = [
1, 2, 3, 4, 5, 6, 9, 10, 12, 15, 18, 20, 25, 30, 40, 45, 50, 60, 75, 80, 90, 100, 150, 200, 250,
300, 350, 400, 450, 500, 600, 700, 800, 900, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000,
6000, 7000, 8000, 9000, 10000
];

const GspPvRemixChart: FC<{
Expand Down Expand Up @@ -51,7 +59,6 @@ const GspPvRemixChart: FC<{
} = useGetGspData(gspId);
// TODO – temp reset; if aggregation is zones, make sure data is all set
if ([NationalAggregation.DNO, NationalAggregation.zone].includes(nationalAggregationLevel)) {
gspLocationInfo = [];
gspNHourData = [];
}
// const gspData = fcAll?.forecasts.find((fc) => fc.location.gspId === gspId);
Expand Down Expand Up @@ -117,7 +124,7 @@ const GspPvRemixChart: FC<{

// set ymax to the installed capacity of the graph
let yMax = gspInstalledCapacity || 100;
yMax = getRoundedTickBoundary(yMax, yMax_levels);
yMax = getRoundedTickBoundary(yMax, Y_MAX_TICKS);

const title =
nationalAggregationLevel === NationalAggregation.GSP ? gspName || "" : String(gspId);
Expand Down Expand Up @@ -166,6 +173,7 @@ const GspPvRemixChart: FC<{
visibleLines={visibleLines}
deltaView={deltaView}
deltaYMaxOverride={Math.ceil(Number(gspInstalledCapacity) / 200) * 100 || 500}
yTicks={getTicks(yMax, Y_MAX_TICKS)}
/>
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ const useGetGspData = (gspId: number | string) => {
const [nHourForecast] = useGlobalState("nHourForecast");
const [nationalAggregationLevel] = useGlobalState("nationalAggregationLevel");
let errors: Error[] = [];
let isZoneAggregation = [NationalAggregation.DNO, NationalAggregation.zone].includes(
nationalAggregationLevel
);

let gspIds: number[] = typeof gspId === "number" ? [gspId] : [];
if (nationalAggregationLevel === NationalAggregation.DNO) {
Expand Down Expand Up @@ -98,9 +101,26 @@ const useGetGspData = (gspId: number | string) => {
const gspForecastDataOneGSP = aggregateForecastData(gspForecastDataOneGSPRaw, gspIds);

//add new useSWR for gspLocationInfo since this is not
const { data: gspLocationInfo, error: gspLocationError } = useLoadDataFromApi<GspEntities>(
`${API_PREFIX}/system/GB/gsp/?gsp_id=${gspId}`
const { data: gspLocationInfoRaw, error: gspLocationError } = useLoadDataFromApi<GspEntities>(
isZoneAggregation
? `${API_PREFIX}/system/GB/gsp/?zones=true` // TODO: API seems to struggle with UI flag if no other query params
: `${API_PREFIX}/system/GB/gsp/?gsp_id=${gspId}`
);
let gspLocationInfo = gspLocationInfoRaw?.filter((gsp) => gspIds.includes(gsp.gspId));
if (isZoneAggregation && gspLocationInfo) {
const zoneCapacity = gspLocationInfo.reduce((acc, gsp) => acc + gsp.installedCapacityMw, 0);
gspLocationInfo = [
{
gspId: gspId as number,
gspName: gspId as string,
regionName: gspId as string,
installedCapacityMw: zoneCapacity,
rmMode: true,
label: "Zone",
gspGroup: "Zone"
}
];
}

// TODO: nHour with aggregation
const nMinuteForecast = nHourForecast * 60;
Expand Down
5 changes: 4 additions & 1 deletion apps/nowcasting-app/components/charts/remix-line.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ type RemixLineProps = {
zoomEnabled?: boolean;
deltaView?: boolean;
deltaYMaxOverride?: number;
yTicks?: number[];
};
const CustomizedLabel: FC<any> = ({
value,
Expand Down Expand Up @@ -142,7 +143,8 @@ const RemixLine: React.FC<RemixLineProps> = ({
visibleLines,
zoomEnabled = true,
deltaView = false,
deltaYMaxOverride
deltaYMaxOverride,
yTicks
}) => {
// Set the y max. If national then set to 12000, for gsp plot use 'auto'
const preppedData = data.sort((a, b) => a.formattedDate.localeCompare(b.formattedDate));
Expand Down Expand Up @@ -412,6 +414,7 @@ const RemixLine: React.FC<RemixLineProps> = ({
yAxisId={"y-axis"}
tick={{ fill: "white", style: { fontSize: "12px" } }}
tickLine={false}
ticks={yTicks}
domain={
globalIsZoomed && view !== VIEWS.SOLAR_SITES
? [0, Number(zoomYMax * 1.1)]
Expand Down
89 changes: 89 additions & 0 deletions apps/nowcasting-app/components/helpers/chartUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,92 @@ export const getZoomYMax = (filteredPreppedData: ChartData[]) => {
.sort((a, b) => Number(b) - Number(a))[0];
}
};

// Function not "in use" but useful for regenerating yMax levels as a constant array for the chart
export const generateYMaxTickArray = () => {
// Generate yMax levels
// Small values
let yMax_levels = Array.from({ length: 4 }, (_, i) => i + 1);
// Multiples of 3
yMax_levels = [...yMax_levels, ...Array.from({ length: 6 }, (_, i) => (i + 1) * 3)];
// Multiples of 5
yMax_levels = [...yMax_levels, ...Array.from({ length: 6 }, (_, i) => (i + 1) * 5)];
// Multiples of 10
yMax_levels = [...yMax_levels, ...Array.from({ length: 5 }, (_, i) => (i + 1) * 10)];
// Multiples of 15
yMax_levels = [...yMax_levels, ...Array.from({ length: 6 }, (_, i) => (i + 1) * 15)];
// Multiples of 20
yMax_levels = [...yMax_levels, ...Array.from({ length: 5 }, (_, i) => (i + 1) * 20)];
// Multiples of 25
yMax_levels = [...yMax_levels, ...Array.from({ length: 3 }, (_, i) => (i + 1) * 25)];
// Multiples of 50
yMax_levels = [...yMax_levels, ...Array.from({ length: 10 }, (_, i) => (i + 1) * 50)];
// Multiples of 100
yMax_levels = [...yMax_levels, ...Array.from({ length: 10 }, (_, i) => (i + 1) * 100)];
// Multiples of 500
yMax_levels = [...yMax_levels, ...Array.from({ length: 10 }, (_, i) => (i + 1) * 500)];
// Multiples of 1000
yMax_levels = [...yMax_levels, ...Array.from({ length: 10 }, (_, i) => (i + 1) * 1000)];
// Remove duplicates
yMax_levels = [...new Set(yMax_levels)];
// Sort
yMax_levels.sort((a, b) => a - b);
return yMax_levels;
};

export const getTicks = (yMax: number, yMax_levels: number[]) => {
const ticks: number[] = [];
const third = yMax / 3;
const quarter = yMax / 4;
const fifth = yMax / 5;
const seventh = yMax / 7;
const testTicksToAdd = (fractionN: number) => {
let canSplit = true;
let tempTicks = [];
for (let i = fractionN; i <= yMax; i += fractionN) {
if (isRoundNumber(i) || i === yMax) {
tempTicks.push(i);
} else {
canSplit = false;
break;
}
}
if (canSplit) {
ticks.push(...tempTicks);
}
};
const isRoundNumber = (n: number) => {
if (n > 2000) {
return n % 500 === 0;
}
if (n > 1000) {
return n % 250 === 0 || n % 100 === 0;
}
if (n > 200) {
return n % 50 === 0;
}
if (n > 20) {
return n % 5 === 0;
}
if (n > 3) {
return n % 1 === 0;
}
return n % 0.5 === 0;
};
if (isRoundNumber(third)) {
testTicksToAdd(third);
}
if (ticks.length === 0 && isRoundNumber(quarter)) {
testTicksToAdd(quarter);
}
if (ticks.length === 0 && isRoundNumber(fifth)) {
testTicksToAdd(fifth);
}
if (ticks.length === 0 && isRoundNumber(seventh)) {
testTicksToAdd(seventh);
}
if (ticks.length === 0) {
testTicksToAdd(yMax > 500 ? 100 : 50);
}
return ticks;
};

0 comments on commit 361b701

Please sign in to comment.