diff --git a/src/components/ui/aladin.tsx b/src/components/ui/aladin.tsx
index be88b98..8d46cc1 100644
--- a/src/components/ui/aladin.tsx
+++ b/src/components/ui/aladin.tsx
@@ -5,14 +5,14 @@ interface AdditionalSource {
ra: number;
dec: number;
label: string;
+ description?: string;
}
interface AladinViewerProps {
- ra?: number;
- dec?: number;
+ ra: number;
+ dec: number;
fov?: number;
survey?: string;
- target?: string;
className?: string;
additionalSources?: AdditionalSource[];
}
@@ -22,7 +22,6 @@ export function AladinViewer({
dec,
fov = 0.5,
survey = "P/DSS2/color",
- target,
className = "w-full h-96",
additionalSources,
}: AladinViewerProps) {
@@ -42,23 +41,35 @@ export function AladinViewer({
showCooGridControl: false,
});
- if (target) {
- aladin.gotoObject(target);
- } else if (ra !== undefined && dec !== undefined) {
- aladin.gotoRaDec(ra, dec);
- }
+ aladin.gotoRaDec(ra, dec);
if (additionalSources && additionalSources.length > 0) {
- const catalog = window.A.catalog({
+ const nameCatalog = window.A.catalog({
labelColumn: "name",
+ shape: "cross",
+ color: "black",
displayLabel: true,
- labelColor: "#fff",
+ labelColor: "lightgrey",
labelFont: "14px sans-serif",
});
- aladin.addCatalog(catalog);
+ const descrCatalog = window.A.catalog({
+ color: "black",
+ shape: "cross",
+ });
+ aladin.addCatalog(nameCatalog);
+ aladin.addCatalog(descrCatalog);
additionalSources.forEach((source) => {
- catalog.addSources(
+ if (source.description) {
+ descrCatalog.addSources([
+ window.A.marker(source.ra, source.dec, {
+ name: source.label,
+ popupTitle: source.label,
+ popupDesc: source.description,
+ }),
+ ]);
+ }
+ nameCatalog.addSources(
window.A.source(source.ra, source.dec, { name: source.label }),
);
});
@@ -66,19 +77,19 @@ export function AladinViewer({
} catch (error) {
console.error("Error initializing Aladin:", error);
}
- }, [ra, dec, fov, survey, target, additionalSources]);
+ }, [ra, dec, fov, survey, additionalSources]);
return
;
}
interface AladinCatalog {
- addSources: (sources: AladinSource) => void;
+ addSources: (sources: AladinSource | AladinSource[]) => void;
}
interface AladinSource {
ra: number;
dec: number;
- properties?: { name?: string };
+ properties?: { name?: string; popupTitle?: string; popupDesc?: string };
}
declare global {
@@ -105,12 +116,20 @@ declare global {
displayLabel?: boolean;
labelColor?: string;
labelFont?: string;
+ sourceSize?: number;
+ shape?: string;
+ color?: string;
}) => AladinCatalog;
source: (
ra: number,
dec: number,
properties?: { name?: string },
) => AladinSource;
+ marker: (
+ ra: number,
+ dec: number,
+ properties?: { name?: string; popupTitle?: string; popupDesc?: string },
+ ) => AladinSource;
};
}
}
diff --git a/src/components/ui/catalog-data.tsx b/src/components/ui/catalog-data.tsx
index d8f9876..f66134e 100644
--- a/src/components/ui/catalog-data.tsx
+++ b/src/components/ui/catalog-data.tsx
@@ -1,139 +1,136 @@
import { ReactElement } from "react";
import { CommonTable } from "./common-table";
-import { PgcObject, Schema } from "../../clients/backend/types.gen";
+import { Catalogs, Schema } from "../../clients/backend/types.gen";
interface CatalogDataProps {
- object: PgcObject;
- schema: Schema | null;
+ catalogs: Catalogs;
+ schema: Schema;
+}
+
+function formatValueWithError(
+ value: number | undefined,
+ error: number | undefined,
+ unit?: string,
+ decimalPlaces: number = 0,
+): string {
+ if (value === undefined) return "NULL";
+ const formattedValue = value.toFixed(decimalPlaces);
+ const formattedError =
+ error?.toFixed(decimalPlaces) || "0".padEnd(decimalPlaces + 1, "0");
+
+ if (!unit) {
+ return `${formattedValue} ± ${formattedError}`;
+ }
+
+ return `${formattedValue} ${unit} ± ${formattedError} ${unit}`;
}
export function CatalogData({
- object,
+ catalogs,
schema,
}: CatalogDataProps): ReactElement {
- if (!object || !schema) return ;
-
- const coordinatesColumns = [
- { name: "Parameter" },
- { name: "Value" },
- { name: "Unit" },
- { name: "Error" },
- { name: "Error unit" },
- ];
-
- const coordinatesData = [
- {
- Parameter: "Right ascension",
- Value: object.catalogs?.coordinates?.equatorial?.ra?.toFixed(6) || "NULL",
- Unit: schema.units.coordinates?.equatorial?.ra || "NULL",
- Error:
- object.catalogs?.coordinates?.equatorial?.e_ra?.toFixed(6) || "NULL",
- "Error unit": schema.units.coordinates?.equatorial?.e_ra || "NULL",
- },
- {
- Parameter: "Declination",
- Value:
- object.catalogs?.coordinates?.equatorial?.dec?.toFixed(6) || "NULL",
- Unit: schema.units.coordinates?.equatorial?.dec || "NULL",
- Error:
- object.catalogs?.coordinates?.equatorial?.e_dec?.toFixed(6) || "NULL",
- "Error unit": schema.units.coordinates?.equatorial?.e_dec || "NULL",
- },
- {
- Parameter: "Galactic longitude",
- Value: object.catalogs?.coordinates?.galactic?.lon?.toFixed(6) || "NULL",
- Unit: schema.units.coordinates?.galactic?.lon || "NULL",
- Error:
- object.catalogs?.coordinates?.galactic?.e_lon?.toFixed(6) || "NULL",
- "Error unit": schema.units.coordinates?.galactic?.e_lon || "NULL",
- },
- {
- Parameter: "Galactic latitude",
- Value: object.catalogs?.coordinates?.galactic?.lat?.toFixed(6) || "NULL",
- Unit: schema.units.coordinates?.galactic?.lat || "NULL",
- Error:
- object.catalogs?.coordinates?.galactic?.e_lat?.toFixed(6) || "NULL",
- "Error unit": schema.units.coordinates?.galactic?.e_lat || "NULL",
- },
- ];
+ if (!catalogs) return ;
- const redshiftColumns = [
- { name: "Parameter" },
- { name: "Value" },
- { name: "Error" },
- ];
+ const columns = [{ name: "Parameter" }, { name: "Value" }];
- const redshiftData = [
- {
- Parameter: "z",
- Value: object.catalogs?.redshift?.z?.toFixed(6) || "NULL",
- Error: object.catalogs?.redshift?.e_z?.toFixed(6) || "NULL",
- },
- ];
+ const data = [];
- const velocityColumns = [
- { name: "Parameter" },
- { name: "Value" },
- { name: "Unit" },
- { name: "Error" },
- { name: "Error unit" },
- ];
+ if (catalogs?.designation?.name) {
+ data.push({
+ Parameter: "Name",
+ Value: catalogs.designation.name,
+ });
+ }
- const velocityData = [
- {
- Parameter: "Heliocentric",
- Value: object.catalogs?.velocity?.heliocentric?.v?.toFixed(2) || "NULL",
- Unit: schema.units.velocity?.heliocentric?.v || "NULL",
- Error: object.catalogs?.velocity?.heliocentric?.e_v?.toFixed(2) || "NULL",
- "Error unit": schema.units.velocity?.heliocentric?.e_v || "NULL",
- },
- {
- Parameter: "Local Group",
- Value: object.catalogs?.velocity?.local_group?.v?.toFixed(2) || "NULL",
- Unit: schema.units.velocity?.local_group?.v || "NULL",
- Error: object.catalogs?.velocity?.local_group?.e_v?.toFixed(2) || "NULL",
- "Error unit": schema.units.velocity?.local_group?.e_v || "NULL",
- },
- {
- Parameter: "CMB (old)",
- Value: object.catalogs?.velocity?.cmb_old?.v?.toFixed(2) || "NULL",
- Unit: schema.units.velocity?.cmb_old?.v || "NULL",
- Error: object.catalogs?.velocity?.cmb_old?.e_v?.toFixed(2) || "NULL",
- "Error unit": schema.units.velocity?.cmb_old?.e_v || "NULL",
- },
- {
- Parameter: "CMB",
- Value: object.catalogs?.velocity?.cmb?.v?.toFixed(2) || "NULL",
- Unit: schema.units.velocity?.cmb?.v || "NULL",
- Error: object.catalogs?.velocity?.cmb?.e_v?.toFixed(2) || "NULL",
- "Error unit": schema.units.velocity?.cmb?.e_v || "NULL",
- },
- ];
+ if (catalogs?.coordinates) {
+ data.push(
+ {
+ Parameter: "Equatorial RA",
+ Value: formatValueWithError(
+ catalogs.coordinates.equatorial?.ra,
+ catalogs.coordinates.equatorial?.e_ra,
+ schema.units.coordinates?.equatorial?.ra,
+ 2,
+ ),
+ },
+ {
+ Parameter: "Equatorial Dec",
+ Value: formatValueWithError(
+ catalogs.coordinates.equatorial?.dec,
+ catalogs.coordinates.equatorial?.e_dec,
+ schema.units.coordinates?.equatorial?.dec,
+ 2,
+ ),
+ },
+ {
+ Parameter: "Galactic l",
+ Value: formatValueWithError(
+ catalogs.coordinates.galactic?.lon,
+ catalogs.coordinates.galactic?.e_lon,
+ schema.units.coordinates?.galactic?.lon,
+ 2,
+ ),
+ },
+ {
+ Parameter: "Galactic b",
+ Value: formatValueWithError(
+ catalogs.coordinates.galactic?.lat,
+ catalogs.coordinates.galactic?.e_lat,
+ schema.units.coordinates?.galactic?.lat,
+ 2,
+ ),
+ },
+ );
+ }
- return (
-
- {object.catalogs?.coordinates && (
-
- Coordinates
- Celestial coordinates of the object
-
- )}
+ if (catalogs?.redshift) {
+ data.push({
+ Parameter: "Redshift z",
+ Value: formatValueWithError(
+ catalogs.redshift.z,
+ catalogs.redshift.e_z,
+ undefined,
+ 5,
+ ),
+ });
+ }
- {object.catalogs?.redshift && (
-
- Redshift
- Redshift measurements
-
- )}
+ if (catalogs?.velocity) {
+ data.push(
+ {
+ Parameter: "Heliocentric Velocity",
+ Value: formatValueWithError(
+ catalogs.velocity.heliocentric?.v,
+ catalogs.velocity.heliocentric?.e_v,
+ schema.units.velocity?.heliocentric?.v,
+ ),
+ },
+ {
+ Parameter: "Local Group Velocity",
+ Value: formatValueWithError(
+ catalogs.velocity.local_group?.v,
+ catalogs.velocity.local_group?.e_v,
+ schema.units.velocity?.local_group?.v,
+ ),
+ },
+ {
+ Parameter: "CMB (old) Velocity",
+ Value: formatValueWithError(
+ catalogs.velocity.cmb_old?.v,
+ catalogs.velocity.cmb_old?.e_v,
+ schema.units.velocity?.cmb_old?.v,
+ ),
+ },
+ {
+ Parameter: "CMB Velocity",
+ Value: formatValueWithError(
+ catalogs.velocity.cmb?.v,
+ catalogs.velocity.cmb?.e_v,
+ schema.units.velocity?.cmb?.v,
+ ),
+ },
+ );
+ }
- {object.catalogs?.velocity && (
-
- Velocity
-
- Velocity measurements with respect to different apexes
-
-
- )}
-
- );
+ return ;
}
diff --git a/src/components/ui/common-table.tsx b/src/components/ui/common-table.tsx
index b8df151..0061498 100644
--- a/src/components/ui/common-table.tsx
+++ b/src/components/ui/common-table.tsx
@@ -52,7 +52,7 @@ export function CommonTable({
{children && (
diff --git a/src/components/ui/footer.tsx b/src/components/ui/footer.tsx
index fa3947a..e99c5ef 100644
--- a/src/components/ui/footer.tsx
+++ b/src/components/ui/footer.tsx
@@ -7,10 +7,16 @@ import { MdKeyboardArrowDown, MdKeyboardArrowUp } from "react-icons/md";
const footerContent = (
- Information:
+ Information:{" "}
+
+ The next generation of the HyperLeda database
+
- Original version:
+ Original version:{" "}
+
+ OHP Mirror
+
);
diff --git a/src/components/ui/hint.tsx b/src/components/ui/hint.tsx
index 513e2e1..879182e 100644
--- a/src/components/ui/hint.tsx
+++ b/src/components/ui/hint.tsx
@@ -1,10 +1,10 @@
import { Tooltip } from "flowbite-react";
-import { ReactElement } from "react";
+import { ReactElement, ReactNode } from "react";
import { MdHelpOutline } from "react-icons/md";
interface HintProps {
children: ReactElement;
- hintContent: ReactElement;
+ hintContent: ReactNode;
className?: string;
}
diff --git a/src/components/ui/link-button.tsx b/src/components/ui/link-button.tsx
deleted file mode 100644
index e09d040..0000000
--- a/src/components/ui/link-button.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { ReactElement, ReactNode } from "react";
-import { MdOpenInNew } from "react-icons/md";
-import { Link } from "./link";
-
-interface LinkButtonProps {
- children?: ReactNode;
- to: string;
-}
-
-export function LinkButton(props: LinkButtonProps): ReactElement {
- return (
-
-
{props.children}
-
-
-
-
- );
-}
diff --git a/src/components/ui/link.tsx b/src/components/ui/link.tsx
index 7999d88..a4b37c6 100644
--- a/src/components/ui/link.tsx
+++ b/src/components/ui/link.tsx
@@ -1,25 +1,35 @@
-import { ReactElement } from "react";
+import { ReactElement, ReactNode } from "react";
+import { MdOpenInNew } from "react-icons/md";
interface LinkProps {
- children?: ReactElement | string;
+ children?: ReactNode;
href: string;
className?: string;
+ external?: boolean;
}
export function Link(props: LinkProps): ReactElement {
- const content = props.children ?? props.href;
+ const content = props.children;
const baseClass = "text-green-500 hover:text-green-600 transition-colors";
const className = props.className
? `${baseClass} ${props.className}`
: baseClass;
+
+ const linkProps = props.external
+ ? {
+ target: "_blank",
+ rel: "noopener noreferrer",
+ }
+ : {};
+
return (
{content}
+ {props.external && }
);
}
diff --git a/src/pages/CrossmatchResults.tsx b/src/pages/CrossmatchResults.tsx
index c232026..0c8b6b3 100644
--- a/src/pages/CrossmatchResults.tsx
+++ b/src/pages/CrossmatchResults.tsx
@@ -20,7 +20,7 @@ import { getResource } from "../resources/resources";
import { Button } from "../components/ui/button";
import { Loading } from "../components/ui/loading";
import { ErrorPage, ErrorPageHomeButton } from "../components/ui/error-page";
-import { LinkButton } from "../components/ui/link-button";
+import { Link } from "../components/ui/link";
export function CrossmatchResultsPage(): ReactElement {
const [searchParams, setSearchParams] = useSearchParams();
@@ -121,9 +121,9 @@ export function CrossmatchResultsPage(): ReactElement {
function getRecordName(record: RecordCrossmatch): ReactElement {
const displayName = record.catalogs.designation?.name || record.record_id;
return (
-
+
{displayName}
-
+
);
}
@@ -224,15 +224,14 @@ export function CrossmatchResultsPage(): ReactElement {
Crossmatch results
-
-
-
+
+
- {object.catalogs?.coordinates?.equatorial && (
+ {object.catalogs?.coordinates && (
{object.catalogs?.designation?.name || `PGC ${object.pgc}`}
- PGC: {object.pgc}
+ PGC: {object.pgc}
+
+ OHP Mirror
+
-
-
+
);
}
diff --git a/src/pages/RecordCrossmatchDetails.tsx b/src/pages/RecordCrossmatchDetails.tsx
index a2ab362..a25d59a 100644
--- a/src/pages/RecordCrossmatchDetails.tsx
+++ b/src/pages/RecordCrossmatchDetails.tsx
@@ -2,31 +2,18 @@ import { ReactElement, useEffect, useState } from "react";
import { NavigateFunction, useNavigate, useParams } from "react-router-dom";
import { AladinViewer } from "../components/ui/aladin";
import { Loading } from "../components/ui/loading";
-import {
- ErrorPage,
- ErrorPageBackButton,
- ErrorPageHomeButton,
-} from "../components/ui/error-page";
+import { ErrorPage, ErrorPageHomeButton } from "../components/ui/error-page";
import { CatalogData } from "../components/ui/catalog-data";
import { getRecordCrossmatchAdminApiV1RecordCrossmatchGet } from "../clients/admin/sdk.gen";
import {
GetRecordCrossmatchResponse,
RecordCrossmatch,
- RecordCrossmatchStatus,
PgcCandidate,
Schema as AdminSchema,
} from "../clients/admin/types.gen";
-import {
- PgcObject,
- Schema as BackendSchema,
-} from "../clients/backend/types.gen";
+import { Schema as BackendSchema } from "../clients/backend/types.gen";
import { getResource } from "../resources/resources";
-
-function backToResultsHandler(navigate: NavigateFunction) {
- return function f() {
- navigate(-1);
- };
-}
+import { Link } from "../components/ui/link";
function renderNotFound(navigate: NavigateFunction) {
return (
@@ -34,16 +21,12 @@ function renderNotFound(navigate: NavigateFunction) {
title="Crossmatch Not Found"
message="The requested crossmatch record could not be found."
>
-
navigate("/")} />
);
}
-function getStatusLabel(status: RecordCrossmatchStatus): string {
- return getResource(`crossmatch.status.${status}`).Title;
-}
-
+// TODO: remove when admin api uses the same structures as data api
function convertAdminSchemaToBackendSchema(
adminSchema: AdminSchema,
): BackendSchema {
@@ -93,90 +76,110 @@ function convertAdminSchemaToBackendSchema(
};
}
-function convertToPgcObject(candidate: PgcCandidate): PgcObject {
- return {
- pgc: candidate.pgc,
- catalogs: candidate.catalogs,
- };
-}
+function createDescription(
+ velocity?: { v: number; e_v: number } | null,
+ redshift?: { z: number; e_z: number } | null,
+): string {
+ const parts = [];
-function convertRecordToPgcObject(record: RecordCrossmatch): PgcObject {
- return {
- pgc: record.metadata.pgc || 0,
- catalogs: record.catalogs,
- };
+ if (velocity) {
+ parts.push(`v: ${velocity.v.toFixed(1)} ± ${velocity.e_v.toFixed(1)} km/s`);
+ }
+
+ if (redshift) {
+ parts.push(`z: ${redshift.z.toFixed(4)} ± ${redshift.e_z.toFixed(4)}`);
+ }
+
+ return parts.join(", ");
}
-function convertCandidatesToAdditionalSources(candidates: PgcCandidate[]) {
- return candidates
+function convertCandidatesToAdditionalSources(
+ candidates: PgcCandidate[],
+ mainRecord: RecordCrossmatch,
+) {
+ const candidateSources = candidates
.filter((candidate) => candidate.catalogs?.coordinates?.equatorial)
.map((candidate) => ({
ra: candidate.catalogs!.coordinates!.equatorial.ra,
dec: candidate.catalogs!.coordinates!.equatorial.dec,
label: `PGC ${candidate.pgc}`,
+ description: createDescription(
+ candidate.catalogs?.velocity?.heliocentric,
+ candidate.catalogs?.redshift,
+ ),
}));
+
+ const mainRecordSource = mainRecord.catalogs?.coordinates?.equatorial
+ ? {
+ ra: mainRecord.catalogs.coordinates.equatorial.ra,
+ dec: mainRecord.catalogs.coordinates.equatorial.dec,
+ label:
+ mainRecord.catalogs?.designation?.name ||
+ `Record ${mainRecord.record_id}`,
+ }
+ : null;
+
+ return mainRecordSource
+ ? [mainRecordSource, ...candidateSources]
+ : candidateSources;
}
function renderCrossmatchDetails(
data: GetRecordCrossmatchResponse,
): ReactElement {
const { crossmatch, candidates, schema } = data;
- const recordObject = convertRecordToPgcObject(crossmatch);
+ const recordCatalogs = crossmatch.catalogs;
const backendSchema = convertAdminSchemaToBackendSchema(schema);
- const candidateSources = convertCandidatesToAdditionalSources(candidates);
+ const candidateSources = convertCandidatesToAdditionalSources(
+ candidates,
+ crossmatch,
+ );
return (
{crossmatch.catalogs?.coordinates?.equatorial && (
-
-
- {candidateSources.length > 0 && (
-
- White labels show crossmatch candidates
-
- )}
-
+
)}
-
- Record ID: {crossmatch.record_id}
-
-
- Status: {getStatusLabel(crossmatch.status)}
-
- {crossmatch.metadata.pgc && (
-
PGC: {crossmatch.metadata.pgc}
+ {crossmatch.catalogs?.designation?.name && (
+
+ {crossmatch.catalogs.designation.name}
+
)}
+
+ Status:{" "}
+ {getResource(`crossmatch.status.${crossmatch.status}`).Title}
+
-
-
Record Catalog Data
-
-
+
{candidates.length > 0 && (
Crossmatch Candidates
- {candidates.map((candidate, index) => {
- const candidateObject = convertToPgcObject(candidate);
- return (
-
-
- Candidate {index + 1}: PGC {candidate.pgc}
-
-
-
- );
- })}
+ {candidates.map((candidate, index) => (
+
+
+ Candidate {index + 1}:{" "}
+ {`PGC ${candidate.pgc}`}
+
+
+
+ ))}
)}
diff --git a/src/pages/TableDetails.tsx b/src/pages/TableDetails.tsx
index 75c1b53..4343f2f 100644
--- a/src/pages/TableDetails.tsx
+++ b/src/pages/TableDetails.tsx
@@ -38,7 +38,10 @@ function renderBibliography(bib: Bibliography): ReactElement {
return (
- {bib.bibcode} | {authors}: "{bib.title}"
+
+ {bib.bibcode}
+ {" "}
+ | {authors}: "{bib.title}"
);
@@ -220,7 +223,7 @@ function ColumnInfo(props: ColumnInfoProps): ReactElement {
Unified Content Descriptor. Describes astronomical quantities in a
structured way. For more information see{" "}
-
+
IVOA Recommendation
.