Skip to content

Commit

Permalink
Add telescope position plot
Browse files Browse the repository at this point in the history
  • Loading branch information
albireox committed Aug 19, 2024
1 parent e7b4b2a commit d2dcb16
Show file tree
Hide file tree
Showing 8 changed files with 1,278 additions and 12 deletions.
3 changes: 3 additions & 0 deletions app/overview/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ActorsTable from '@/src/components/APITables/ActorsTable/ActorsTable';
import EnclosureTable from '@/src/components/APITables/EnclosureTable/EnclosureTable';
import EphemerisTable from '@/src/components/APITables/EphemerisTable/EphemerisTable';
import SpecTable from '@/src/components/APITables/SpecTable/SpecTable';
import TelescopesTable from '@/src/components/APITables/TelescopesTable/TelescopesTable';
import WeatherTable from '@/src/components/APITables/WeatherTable/WeatherTable';
import { Box, SimpleGrid, Stack, useMatches } from '@mantine/core';
import React from 'react';
Expand All @@ -30,6 +31,7 @@ export default function OverviewPage() {
</Stack>
<Stack gap="lg">
<EnclosureTable />
<TelescopesTable />
<EphemerisTable />
</Stack>
</>
Expand All @@ -39,6 +41,7 @@ export default function OverviewPage() {
<>
<Stack gap="lg">
<SpecTable />
<TelescopesTable />
<WeatherTable />
</Stack>
<Stack gap="lg">
Expand Down
19 changes: 18 additions & 1 deletion app/telescopes/[tel]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@

'use client';

import TelescopePositionPlot from '@/src/components/TelescopePositionPlot/TelescopePositionPlot';
import { Box, Stack, Title } from '@mantine/core';
import React from 'react';

export default function TelescopePage({ params }: { params: { tel: string } }) {
const [valid, setValid] = React.useState<boolean>(true);
const [src, setSrc] = React.useState<string | undefined>(undefined);
const [title, setTitle] = React.useState<string | undefined>(undefined);

Expand Down Expand Up @@ -41,11 +43,26 @@ export default function TelescopePage({ params }: { params: { tel: string } }) {
setTitle('Sky-E telescope');
break;
default:
setTitle('Unknown telescope');
setValid(false);
}
}
}, [tel]);

if (tel == 'position') {
return (
<Stack p={8} mt={2} gap="lg">
<Title order={1}>Telescope Position</Title>
<Box ta="center" pt={24}>
<TelescopePositionPlot size="large" />
</Box>
</Stack>
);
}

if (!valid) {
return <h1>404 - Page Not Found</h1>;
}

return (
<>
<Stack p={8} mt={2} gap="lg">
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@next/bundle-analyzer": "^14.2.5",
"@tabler/icons": "^3.12.0",
"@tabler/icons-react": "^3.12.0",
"@visx/visx": "^3.11.0",
"next": "14.2.5",
"react": "18.3.1",
"react-dom": "18.3.1",
Expand Down
35 changes: 26 additions & 9 deletions src/components/APITable/APITable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import classses from './APITable.module.css';

type Element = {
key: string;
label: string;
label: string | undefined;
value: any | undefined;
unit?: string;
valign?: string;
Expand Down Expand Up @@ -107,14 +107,31 @@ export default function APITable(props: {
}
}, [noData]);

const rows = elements.map((element) => (
<Table.Tr key={element.key}>
<Table.Td valign={(element.valign as (typeof Table.Td)['valign']) || 'top'}>
<Text size="sm">{element.label}</Text>
</Table.Td>
<Table.Td>{getValue(element)}</Table.Td>
</Table.Tr>
));
const rows = elements.map((element) => {
let value = getValue(element);
let colspan = 1;
let isSpan = false;

if (!element.label) {
colspan = 2;
isSpan = true;
value = element.value;
}

return (
<Table.Tr key={element.key}>
<Table.Td
valign={(element.valign as (typeof Table.Td)['valign']) || 'top'}
colSpan={colspan}
ta={isSpan ? 'center' : undefined}
autoFocus={false}
>
<Text size="sm">{isSpan ? value : element.label}</Text>
</Table.Td>
{!isSpan && <Table.Td>{getValue(element)}</Table.Td>}
</Table.Tr>
);
});

return (
<>
Expand Down
24 changes: 24 additions & 0 deletions src/components/APITables/TelescopesTable/TelescopesTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* @Author: José Sánchez-Gallego (gallegoj@uw.edu)
* @Date: 2024-08-18
* @Filename: TelescopesTable.tsx
* @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)
*/

'use client';

import { IconTelescope } from '@tabler/icons-react';
import APITable from '../../APITable/APITable';
import TelescopePositionPlot from '../../TelescopePositionPlot/TelescopePositionPlot';

export default function TelescopesTable() {
const elements = [
{
key: 'position',
label: undefined,
value: <TelescopePositionPlot size="small" />,
},
];

return <APITable title="Telescopes" elements={elements} icon={<IconTelescope />} />;
}
1 change: 1 addition & 0 deletions src/components/LVMWebRoot/LVMAppShell/NavBar/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default function NavBar() {

<Box p={8} />
<NavAccordion value="Telescopes" open icon={IconTelescope}>
<NavAccordionItem value="Position" href="/telescopes/position" />
<NavAccordionItem value="Science" href="/telescopes/sci" />
<NavAccordionItem value="Spec" href="/telescopes/spec" />
<NavAccordionItem value="Sky-E" href="/telescopes/skye" />
Expand Down
218 changes: 218 additions & 0 deletions src/components/TelescopePositionPlot/TelescopePositionPlot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/*
* @Author: José Sánchez-Gallego (gallegoj@uw.edu)
* @Date: 2024-08-18
* @Filename: TelescopePositionPlot.tsx
* @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)
*/

// Adapted from https://airbnb.io/visx/lineradial

import useAPICall from '@/src/hooks/use-api-call';
import { Tooltip } from '@mantine/core';
import { AxisLeft } from '@visx/axis';
import { LinearGradient } from '@visx/gradient';
import { GridAngle, GridRadial } from '@visx/grid';
import { Group } from '@visx/group';
import { scaleLinear } from '@visx/scale';
import React from 'react';

const green = '#e5fd3d';
export const blue = '#aeeef8';
export const background = '#744cca';
const strokeColor = '#744cca';

type TelescopeCoordinates = {
alt: number;
az: number;
ra: number;
dec: number;
};

type Telescopes = 'sci' | 'spec' | 'skye' | 'skyw';

export type TelescopesCoordinates = {
[tel in Telescopes]: TelescopeCoordinates;
};

// scales (just for the grids)
const xScale = scaleLinear<number>({
range: [0, Math.PI * 2],
domain: [0, Math.PI * 2],
});

const yScale = scaleLinear<number>({
domain: [90, 30],
});

const defaultSizes = {
large: [1000, 1000, 20],
small: [250, 250, 10],
};

const telescopeEmojis: { [tel in Telescopes]: string } = {
sci: '🔭',
spec: '⭐️',
skye: '⚫️',
skyw: '⬛️',
};

const telescopeToName: { [tel in Telescopes]: string } = {
sci: 'Science',
spec: 'Spec',
skye: 'Sky-E',
skyw: 'Sky-W',
};

type TelescopePositionPlotProps = {
size?: 'large' | 'small';
};

export default function TelescopePositionPlot(props: TelescopePositionPlotProps) {
const { size = 'large' } = props;
const [width, height, padding] = defaultSizes[size];

const [coordinates, , , refresh] = useAPICall<TelescopesCoordinates>(
'/telescopes/coordinates',
{ interval: 30000 }
);

const polar_to_cartesian = React.useCallback(
(r: number, theta: number) => {
// r is 0 at 90 deg and 1 at 30 deg
const rr = 3 / 2 - r / 60;
// theta is in degrees, convert to radians and rotate for North up, East left
const tt = -(Math.PI / 180) * theta - Math.PI / 2;

return {
x: rr * Math.cos(tt) * (width / 2 - padding),
y: rr * Math.sin(tt) * (height / 2 - padding),
};
},
[width, height, padding]
);

const azTicks = React.useMemo(() => {
const ticks = ['N', 'E', 'S', 'W'];
const values = [0, 90, 180, 270];
return ticks.map((tick, i) => {
const { x, y } = polar_to_cartesian(28.5, values[i]);
return [x, y, tick];
});
}, [padding]);

const telCoordinates = React.useMemo(() => {
if (!coordinates) return {};

const valid = Object.fromEntries(
Object.entries(coordinates).filter(([tel, value]) => value.alt > 30)
);

return Object.fromEntries(
Object.entries(valid).map(([tel, value]) => [
tel,
polar_to_cartesian(value.alt, value.az),
])
);
}, [coordinates]);

// Set the scale for the figure size and reverse it for the grid.
yScale.range([0, height / 2 - padding]);
const reverseYScale = yScale.copy().range(yScale.range().reverse());

return (
<svg width={width} height={height} onClick={refresh}>
<LinearGradient from={green} to={blue} id="line-gradient" />
<rect width={width} height={height} fill="transparent" rx={14} />
<Group top={height / 2} left={width / 2}>
<GridAngle
scale={xScale}
outerRadius={height / 2 - padding}
stroke={green}
strokeWidth={1}
strokeOpacity={0.3}
strokeDasharray="5,2"
tickValues={[...Array(8)].map((_, i) => (i / 4) * Math.PI)}
/>
<GridRadial
scale={reverseYScale}
numTicks={7}
stroke={blue}
strokeWidth={1}
fill={blue}
fillOpacity={0.1}
strokeOpacity={0.2}
/>
<AxisLeft
top={-height / 2 + padding}
scale={reverseYScale}
numTicks={7}
tickStroke="none"
tickLabelProps={{
fontSize: 10,
fill: blue,
fillOpacity: 1,
textAnchor: 'middle',
dx: '1em',
dy: '-0.5em',
stroke: strokeColor,
strokeWidth: 0.5,
paintOrder: 'stroke',
}}
tickValues={size === 'large' ? [40, 50, 60, 70, 80] : []}
hideAxisLine
/>
{size === 'large' &&
azTicks.map(([x, y, tick]) => (
<text
key={tick}
x={x}
y={y}
fontSize={14}
fontWeight={700}
textAnchor="middle"
fill={blue}
fillOpacity={1}
dy="0.25em"
>
{tick}
</text>
))}
{Object.entries(telCoordinates).map(([tel, { x, y }]) => (
<Group key={`telescope-Group-${tel}`}>
<Tooltip
label={telescopeToName[tel as Telescopes]}
key={`telescope-tooltip-${tel}`}
>
<text
key={`telescope-emoji-${tel}`}
x={x}
y={y}
fontSize={size === 'large' ? 30 : 15}
textAnchor="middle"
fill={green}
fillOpacity={1}
dy="0.25em"
>
{telescopeEmojis[tel as Telescopes]}
</text>
</Tooltip>
{size === 'large' && (
<text
key={`telescope-name-${tel}`}
x={x}
y={y + 25}
fontSize={20}
textAnchor="middle"
fill={green}
fillOpacity={1}
dy="0.25em"
>
{telescopeToName[tel as Telescopes]}
</text>
)}
</Group>
))}
</Group>
</svg>
);
}
Loading

0 comments on commit d2dcb16

Please sign in to comment.