diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index c4707134..7029433a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -42,7 +42,7 @@ jobs: - name: setup Node/npm uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 22 cache: 'npm' cache-dependency-path: 'main/src/main/webui/package-lock.json' diff --git a/src/main/webui/package-lock.json b/src/main/webui/package-lock.json index 98e00bee..d255a509 100644 --- a/src/main/webui/package-lock.json +++ b/src/main/webui/package-lock.json @@ -17,6 +17,7 @@ "@popperjs/core": "^2.11.8", "@tabler/icons-react": "^2.34.0", "@tanstack/react-query": "^5.28.8", + "@tsastro/astrolib": "^0.2.0", "@types/prismjs": "^1.26.4", "@types/react-latex": "^2.0.3", "@types/react-syntax-highlighter": "^15.5.13", @@ -527,6 +528,12 @@ "node": ">=6.9.0" } }, + "node_modules/@buge/ts-units": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@buge/ts-units/-/ts-units-1.2.3.tgz", + "integrity": "sha512-3aatF0mnIxZBMkL2EkKnOEbVAFeZR8oCUwXNTUyOv7hxeCuA8fycVWaVWBiAWdnmMTmLL5KAgzMiQ0MreuaYJg==", + "license": "Apache-2.0" + }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -2122,6 +2129,22 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@tsastro/astrolib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@tsastro/astrolib/-/astrolib-0.2.0.tgz", + "integrity": "sha512-vzpmXWpY43a6Zmh4x4M9kkeQnZG0yBpghe73eyQ0B3SpuAFjBpGOgMiKtqq7UefrAvxVMzmnoMxTIclXaPM62Q==", + "dependencies": { + "@buge/ts-units": "^1.2.3", + "@tsastro/tsofa": "^18.1.0", + "common-js": "^0.3.8" + } + }, + "node_modules/@tsastro/tsofa": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/@tsastro/tsofa/-/tsofa-18.1.0.tgz", + "integrity": "sha512-P9PSt8KGlA8azf+3181IewkkbcmhvbpsKXRhjqw5qoRgcTYGhfGW80xnm3FWcA0rUGdfB5sPvmE2J/kOLX5AGQ==", + "license": "MIT" + }, "node_modules/@types/aria-query": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", @@ -3354,6 +3377,12 @@ "node": ">= 12" } }, + "node_modules/common-js": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/common-js/-/common-js-0.3.8.tgz", + "integrity": "sha512-83poCK3wlRnYqWdolJiYrMYvaBFzlDO1RwQVti2ZDBR2K5ifBGksu2cw3ZKFioEqB8FVLUmuu3OXxFUlSBk5UQ==", + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", diff --git a/src/main/webui/package.json b/src/main/webui/package.json index a61d8606..99c8c4f3 100644 --- a/src/main/webui/package.json +++ b/src/main/webui/package.json @@ -5,6 +5,7 @@ "type": "module", "homepage": "https://kilburn.jb.man.ac.uk/pst/gui/", "dependencies": { + "@tsastro/astrolib": "^0.2.0", "@mantine/core": "^7.13.1", "@mantine/dates": "^7.13.1", "@mantine/form": "^7.13.1", diff --git a/src/main/webui/src/ProposalEditorView/targets/New.tsx b/src/main/webui/src/ProposalEditorView/targets/New.tsx index be02f613..83fb7ea8 100644 --- a/src/main/webui/src/ProposalEditorView/targets/New.tsx +++ b/src/main/webui/src/ProposalEditorView/targets/New.tsx @@ -1,4 +1,4 @@ -import {Modal, NumberInput, TextInput, Stack, Fieldset, Grid, rem, Text} from "@mantine/core"; +import {Modal, TextInput, Stack, Fieldset, Grid, rem, Text} from "@mantine/core"; import {useForm} from "@mantine/form"; import {useDisclosure, useMediaQuery} from "@mantine/hooks"; import { @@ -34,6 +34,7 @@ import {notifyError, notifySuccess} from "../../commonPanel/notifications.tsx"; import getErrorMessage from "../../errorHandling/getErrorMessage.tsx"; import {SimbadSearch} from "./simbadSearch.tsx"; import SimbadSearchHelp from "./simbadSearchHelp.tsx"; +import { AstroLib } from "@tsastro/astrolib"; export let Aladin: AladinType; @@ -88,8 +89,8 @@ const TargetForm = (props: {closeModal: () => void}): ReactElement => { const form = useForm({ initialValues: { TargetName: "", - RA: 0.00, - Dec: 0.00, + RA: "0.00", + Dec: "0.00", SelectedEpoch: "J2000", sexagesimal: "00:00:00 +00:00:00" }, @@ -117,11 +118,11 @@ const TargetForm = (props: {closeModal: () => void}): ReactElement => { coordSys: {val: "ICRS"}, lat: { "@type": "ivoa:RealQuantity", - value: val.RA, unit: { value: "degrees" } + value: parseFloat(val.RA), unit: { value: "degrees" } }, lon: { "@type": "ivoa:RealQuantity", - value: val.Dec, unit: { value: "degrees" } + value: parseFloat(val.Dec), unit: { value: "degrees" } } } const Target: CelestialTarget = { @@ -190,8 +191,55 @@ const TargetForm = (props: {closeModal: () => void}): ReactElement => { const HandleEvent = (event: MouseEvent) => { const [ra, dec] = GetOffset(event); const [raCoords, decCoords] = Aladin.pix2world(ra, dec); - form.setFieldValue('RA', raCoords); - form.setFieldValue('Dec', decCoords); + form.setFieldValue('RA', raCoords.toString()); + form.setFieldValue('Dec', decCoords.toString()); + + //we want to update the name if there is no entry OR if the entry is from the catalogue + if (form.values["TargetName"].slice(0,6) != "Target" && form.values["TargetName"].slice(0,8) != "Modified") + { + //if we have a catalogue name, modify it to show that the target has moved + if(form.values["TargetName"] != ""){ + ModifyTargetName(form.values["TargetName"]); + } + //if we have no name, add one + else{ + GenerateTargetDefaultName(); + } + + } + } + + /** + * generate new default name for a target + */ + const GenerateTargetDefaultName = () => { + //BJLG + //reset the name to something generic + random suffix + let targetProxyName = "Target_"; + let randNum = Math.random(); + //convert the number into something using chars 0-9 A-Z + let hexString = randNum.toString(36); + targetProxyName += hexString.slice(6).toUpperCase(); + form.setFieldValue('TargetName', targetProxyName); + + //allow submission in case this was previously locked + setNameUnique(true); + } + /** + * modify the name for a target + */ + const ModifyTargetName = (currentName :string) => { + //BJLG + //reset the name to something generic + random suffix + let targetProxyName = "Modified " + currentName + "_"; + let randNum = Math.random(); + //convert the number into something using chars 0-9 A-Z + let hexString = randNum.toString(36); + targetProxyName += hexString.slice(6).toUpperCase(); + form.setFieldValue('TargetName', targetProxyName); + + //allow submission in case this was previously locked + setNameUnique(true); } /** @@ -200,7 +248,7 @@ const TargetForm = (props: {closeModal: () => void}): ReactElement => { */ const UpdateAladinRA = (value: number | string) => { // acquire the aladin object and set it. - Aladin.gotoRaDec(value as number, form.values.Dec); + Aladin.gotoRaDec(value as number, Number(form.values.Dec)); } /** @@ -209,7 +257,7 @@ const TargetForm = (props: {closeModal: () => void}): ReactElement => { */ const UpdateAladinDec = (value: number | string) => { // acquire the aladin object and set it. - Aladin.gotoRaDec(form.values.RA, value as number); + Aladin.gotoRaDec(Number(form.values.RA), value as number); } const responsiveSpan = {base: 2, md: 1} @@ -244,37 +292,87 @@ const TargetForm = (props: {closeModal: () => void}): ReactElement => { }} /> - { - UpdateAladinRA(e); if (form.getInputProps("RA").onChange) { form.getInputProps("RA").onChange(e); - }}} + } + //get the live value from the input + let raValue: string = form.getValues()["RA"]; + + const regexp = new RegExp(/(\d{1,3})\D(\d{1,2})\D(\d{1,2}(\.\d+)[sS]*)/); + //first filter to ensure input is at least in the ball park of sensible + if(regexp.test(raValue)) + { + //convert Sexagemsimal to degrees (Sxg representation is displayed separately) + raValue = String(AstroLib.HmsToDeg(raValue)); + //update the value to degrees + form.setFieldValue("RA", raValue); + } + //if we don't have a name for this object, generate one + if(form.values["TargetName"] == "") + { + GenerateTargetDefaultName(); + } + //update the Aladdin viewport + UpdateAladinRA(Number(raValue)); + }} + + /> - + {AstroLib.DegToHms(Number(form.getValues()["RA"]),3)} + + { - UpdateAladinDec(e); if (form.getInputProps("Dec").onChange) { form.getInputProps("Dec").onChange(e); - }}} + } + //get the live value from the input + let decValue: string = form.getValues()["Dec"]; + + const regexp = new RegExp(/(\d{1,2})\D(\d{1,2})\D(\d{1,2}(\.\d+)[sS]*)/); + //first filter to ensure input is at least in the ball park of sensible + if(regexp.test(decValue)) + { + //convert Sexagemsimal to degrees (Sxg representation is displayed separately) + decValue = String(AstroLib.DmsToDeg(decValue)); + //update the value to degrees + form.setFieldValue("Dec", decValue); + } + //if we don't have a name for this object, generate one + if(form.values["TargetName"] == "") + { + GenerateTargetDefaultName(); + } + //update the Aladdin viewport + UpdateAladinDec(Number(decValue)); + }} /> + + {AstroLib.DegToDms(Number(form.getValues()["Dec"]),3)} +