Skip to content

Commit

Permalink
Thchang/2366 support image coordinates (#2388)
Browse files Browse the repository at this point in the history
* support image coordinates for images with valid WCS headers

* set labels of pixel frames

* disable wcs inputs when the system coordinates is IMG

* minor change for AST plot speed

* fix regression of missing AST grid for no wcs info frame after swithing active frames

* fix warning of searching radius in degree for pixel coordinate frames

* fix spatial matching for the FrameSet's current frame

* fixed relative coordinates for pixel coordinates

* fixed image coordinates for non-squre pixels images

* uppercased image system type and minorly refactored

* fixed CatalogOnlineQueryComponent and genRegionWcsProperties

* fixed the bug of non-wcs images with wcs system in overlay

* fixed the error of switching system when relative system is activated

* changed back switching active frame behaviour

* fixed special non-square pixels images

* added back the frame in Dummy FrameSet

* store relative pixel AST Frame in the third frame of wcsInfoShifted AST FrameSet

* fix the plotting of relative image coordinates for secondary images in the spatial matching

* minor fix

* fix the catalog online query button

* changelog updated

* modified labels of image coordinates with and without offsets

* fixed spatial matching on append when the image coordinates are selected

* disabled spatial matching in image coordinates if images cannot be matched in world coordinates

* added back fix of spatial matching on append

* disabling spatial matching when switching system between world and image coordinates

* added alert

* removed the spatial matching restriction of wcs images cannot be matched in world coordinates

* modified alert context

* fixed catalog plotting in pixel coordinates

* minor change

* minor adjustments

* removed logs
  • Loading branch information
TienHao authored Dec 24, 2024
1 parent fb40277 commit e22bf70
Show file tree
Hide file tree
Showing 14 changed files with 113 additions and 36 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Added a button for deleting all regions ([#1040](https://github.com/CARTAvis/carta-frontend/issues/1040)).
* Added support for loading remote FITS files from the hips2fits server ([#1379](https://github.com/CARTAvis/carta-backend/issues/1379)).
* Supported the customized rest frequency for the moment maps ([[#2396](https://github.com/CARTAvis/carta-frontend/issues/2396)]).
* Supported image coordinates for images with valid WCS headers ([[#2366](https://github.com/CARTAvis/carta-frontend/issues/2366)]).
### Fixed
* Fixed ruler annotation matching bug ([#2242](https://github.com/CARTAvis/carta-frontend/issues/2242)).
* Fixed compass and ruler annotations update bug in the spatially matched image when changing the coordinate ([#2270](https://github.com/CARTAvis/carta-frontend/issues/2270)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export class CatalogQueryComponent extends React.Component {
</FormGroup>
<FormGroup inline={false} label="Center coordinates" disabled={disable}>
<Select
items={Object.keys(SystemType).map(key => SystemType[key])}
items={Object.values(SystemType).filter(sys => sys !== SystemType.Image)}
activeItem={null}
onItemSelect={type => appStore.overlayStore.global.setSystem(type)}
itemRenderer={this.renderSysTypePopOver}
Expand Down Expand Up @@ -236,7 +236,9 @@ export class CatalogQueryComponent extends React.Component {
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<AnchorButton intent={Intent.WARNING} disabled={!configStore.isQuerying} onClick={() => CatalogApiService.Instance.cancelQuery(configStore.catalogDB)} text={"Cancel"} />
{configStore.enableLoadVizier ? <AnchorButton intent={Intent.PRIMARY} disabled={disable} onClick={() => this.loadVizierCatalogs()} text={"Load selected"} /> : null}
<AnchorButton intent={Intent.SUCCESS} disabled={disable} onClick={() => this.query()} text={"Query"} />
<Tooltip content={"Please select WCS coordinates"} disabled={appStore.overlayStore.isWcsCoordinates} position={Position.BOTTOM} hoverOpenDelay={300}>
<AnchorButton intent={Intent.SUCCESS} disabled={disable || appStore.overlayStore.isImgCoordinates} onClick={() => this.query()} text={"Query"} />
</Tooltip>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import moment from "moment/moment";

import {DraggableDialogComponent} from "components/Dialogs";
import {WorkspaceListItem} from "models";
import {AppStore, DialogId, HelpType} from "stores";
import {AlertStore, AppStore, DialogId, HelpType} from "stores";

import {AppToaster, ErrorToast, SuccessToast} from "../../Shared";

Expand Down Expand Up @@ -67,6 +67,12 @@ export const WorkspaceDialogComponent = observer(() => {
return;
}

// TODO: to be removed after storing SystemType in workspace
if (appStore.overlayStore.isImgCoordinates && appStore.frames.map(frame => frame.spatialReference !== null).includes(true)) {
AlertStore.Instance.showAlert("Saving workspace failed: not supporting spatial matching in image cooordinates.");
return;
}

setIsFetching(true);
try {
const res = await appStore.saveWorkspace(name);
Expand Down
8 changes: 4 additions & 4 deletions src/components/ImageView/Overlay/OverlayComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ export class OverlayComponent extends React.Component<OverlayComponentProps> {
const scaleMapping = AST.scaleMap2D(1.0, 1.0 / frame.aspectRatio);
const newFrame = AST.frame(2, "Domain=PIXEL");
AST.addFrame(tempWcsInfo, 1, scaleMapping, newFrame);
AST.setI(tempWcsInfo, "Base", 3);
AST.setI(tempWcsInfo, "Current", 2);
AST.setI(tempWcsInfo, "Base", frame.isOffsetCoord ? 4 : 3);
AST.setI(tempWcsInfo, "Current", frame.isOffsetCoord && OverlayStore.Instance.isImgCoordinates ? 3 : 2);
}

if (frame.isOffsetCoord) {
if (frame.isOffsetCoord && OverlayStore.Instance.isWcsCoordinates) {
const fovSizeInArcsec = frame.getWcsSizeInArcsec(frame.fovSize);
const viewSize = fovSizeInArcsec.x > fovSizeInArcsec.y ? fovSizeInArcsec.y : fovSizeInArcsec.x;
const factor = 2; // jump factor
Expand Down Expand Up @@ -214,7 +214,7 @@ export class OverlayComponent extends React.Component<OverlayComponentProps> {
const formatStringX = this.props.overlaySettings.numbers.formatStringX;
const formatStyingY = this.props.overlaySettings.numbers.formatStringY;
const explicitSystem = this.props.overlaySettings.global.explicitSystem;
if (formatStringX !== undefined && formatStyingY !== undefined && explicitSystem !== undefined) {
if (formatStringX !== undefined && formatStyingY !== undefined && explicitSystem !== undefined && OverlayStore.Instance.isWcsCoordinates && frame.validWcs) {
AST.set(frame.wcsInfo, `Format(${frame.dirX})=${formatStringX}, Format(${frame.dirY})=${formatStyingY}, System=${explicitSystem},` + dirAxesSetting);
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/components/ImageView/Toolbar/ToolbarComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export class ToolbarComponent extends React.Component<ToolbarComponentProps> {
[SystemType.FK4, "FK4"],
[SystemType.Galactic, "GAL"],
[SystemType.Ecliptic, "ECL"],
[SystemType.ICRS, "ICRS"]
[SystemType.ICRS, "ICRS"],
[SystemType.Image, "IMG"]
]);

private static readonly CoordinateSystemTooltip = new Map<SystemType, string>([
Expand Down Expand Up @@ -174,6 +175,7 @@ export class ToolbarComponent extends React.Component<ToolbarComponentProps> {
<MenuItem text={ToolbarComponent.CoordinateSystemName.get(SystemType.Galactic)} onClick={() => this.handleCoordinateSystemClicked(SystemType.Galactic)} />
<MenuItem text={ToolbarComponent.CoordinateSystemName.get(SystemType.Ecliptic)} onClick={() => this.handleCoordinateSystemClicked(SystemType.Ecliptic)} />
<MenuItem text={ToolbarComponent.CoordinateSystemName.get(SystemType.ICRS)} onClick={() => this.handleCoordinateSystemClicked(SystemType.ICRS)} />
<MenuItem text={ToolbarComponent.CoordinateSystemName.get(SystemType.Image)} onClick={() => this.handleCoordinateSystemClicked(SystemType.Image)} />
<FormGroup inline={false} className="offset-group">
<Switch className="offset-switch" disabled={frame.isPVImage || frame.isSwappedZ || frame.isUVImage} checked={frame.isOffsetCoord} onChange={frame.toggleOffsetCoord} label="Offset" />
<Collapse isOpen={frame.isOffsetCoord}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,6 @@ export const CoordNumericInput = ({coord, inputType, value, onChange, valueWcs,
if (coord === CoordinateMode.Image) {
return <ImageCoordNumericInput inputType={inputType} value={value} onChange={onChange} disabled={disabled} customPlaceholder={customPlaceholder} />;
} else {
return <WcsCoordNumericInput inputType={inputType} valueWcs={valueWcs} onChangeWcs={onChangeWcs} disabled={disabled || wcsDisabled} customPlaceholder={customPlaceholder} />;
return <WcsCoordNumericInput inputType={inputType} valueWcs={valueWcs} onChangeWcs={onChangeWcs} disabled={disabled || wcsDisabled || AppStore.Instance.overlayStore.isImgCoordinates} customPlaceholder={customPlaceholder} />;
}
};
3 changes: 3 additions & 0 deletions src/stores/Catalog/CatalogStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ export class CatalogStore {
let yFraction = CatalogStore.GetFractionFromUnit(yUnit.toLocaleLowerCase());

let wcsCopy = AST.copy(wcsInfo);
if (wcsCopy !== 0 && AppStore.Instance.overlayStore.isImgCoordinates) {
AST.setI(wcsCopy, "Current", 2);
}
let system = "System=" + catalogFrame;
AST.set(wcsCopy, system);
if (catalogFrame === CatalogSystemType.FK4) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ export class CatalogOnlineQueryConfigStore {

@computed get searchRadiusInDegree(): number {
const activeFrame = this.activeFrame;
if (activeFrame) {
if (activeFrame?.validWcs && OverlayStore.Instance.isWcsCoordinates) {
const requiredFrameView = activeFrame.requiredFrameView;
const diagonal1 = this.calculateDistanceFromPixelCoord({x: requiredFrameView.xMax, y: requiredFrameView.yMax}, {x: requiredFrameView.xMin, y: requiredFrameView.yMin}, true);
const diagonal2 = this.calculateDistanceFromPixelCoord({x: requiredFrameView.xMin, y: requiredFrameView.yMax}, {x: requiredFrameView.xMax, y: requiredFrameView.yMin}, true);
Expand Down
38 changes: 31 additions & 7 deletions src/stores/Frame/FrameStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,8 @@ export class FrameStore {
}
}

this.updateWcsSystem(this.overlayStore.numbers.formatStringX, this.overlayStore.numbers.formatStringY, this.overlayStore.global.explicitSystem); // for image coordinates selected

if (!this.wcsInfo) {
this.logStore.addWarning(`Problem processing headers in file ${this.filename} for AST`, ["ast"]);
this.wcsInfo = AST.initDummyFrame();
Expand Down Expand Up @@ -1506,9 +1508,21 @@ export class FrameStore {

updateWcsSystem = (formatStringX: string, formatStyingY: string, explicitSystem: SystemType) => {
if (formatStringX !== undefined && formatStyingY !== undefined && explicitSystem !== undefined) {
if (!(this.isPVImage && this.spectralAxis?.valid) && !(this.isSwappedZ && this.spectralAxis?.valid)) {
if (this.validWcs && this.wcsInfo) {
if (!(this.isPVImage && this.spectralAxis?.valid) && !(this.isSwappedZ && this.spectralAxis?.valid) && this.validWcs && this.wcsInfo) {
if (explicitSystem === SystemType.Image) {
// Use base frame for image coordinates
AST.setI(this.wcsInfo, "Current", 1);
if (this.wcsInfoShifted) {
// Use third frame for shifted image coordinates
AST.setI(this.wcsInfoShifted, "Current", 3);
}
} else {
AST.setI(this.wcsInfo, "Current", 2);
AST.set(this.wcsInfo, `Format(${this.dirX})=${formatStringX}, Format(${this.dirY})=${formatStyingY}, System=${explicitSystem}`);
if (this.wcsInfoShifted) {
AST.setI(this.wcsInfoShifted, "Current", 2);
AST.set(this.wcsInfoShifted, `Format(${this.dirX})=${formatStringX}, Format(${this.dirY})=${formatStyingY}, System=${explicitSystem}`);
}
}
}
}
Expand Down Expand Up @@ -1920,7 +1934,7 @@ export class FrameStore {
let cursorPosWCS, cursorPosFormatted;
let precisionX = 0;
let precisionY = 0;
if (this.validWcs || this.isYX || this.isPVImage || this.isUVImage || this.isSwappedZ) {
if (((this.validWcs || this.isYX) && this.overlayStore.isWcsCoordinates) || this.isPVImage || this.isUVImage || this.isSwappedZ) {
// We need to compare X and Y coordinates in both directions
// to avoid a confusing drop in precision at rounding threshold
const offsetBlock = [
Expand Down Expand Up @@ -2095,7 +2109,7 @@ export class FrameStore {

public genRegionWcsProperties = (regionType: CARTA.RegionType, controlPoints: Point2D[], rotation: number, regionId: number = -1): string => {
const centerPoint = controlPoints[CENTER_POINT_INDEX];
if (!this.validWcs || !isFinite(centerPoint.x) || !isFinite(centerPoint.y)) {
if (!this.validWcs || !isFinite(centerPoint.x) || !isFinite(centerPoint.y) || this.overlayStore.isImgCoordinates) {
return "Invalid";
}

Expand Down Expand Up @@ -2178,14 +2192,24 @@ export class FrameStore {
this.spatialReference.createWcsInfoShifted();
} else {
if (this.wcsInfo && this.offsetCenter) {
if (this.wcsInfoShifted) {
AST.deleteObject(this.wcsInfoShifted);
}

const centerInRad = getUnformattedWCSPoint(this.wcsInfo, this.offsetCenter);

if (centerInRad) {
this.wcsInfoShifted = AST.createShiftmapFrameset(this.wcsInfo, centerInRad.x, centerInRad.y);
this.wcsInfoShifted = AST.createShiftmapFrameset(this.wcsInfo, centerInRad.x, centerInRad.y, this.offsetCenter.x, this.offsetCenter.y);
for (const frame of this.secondarySpatialImages) {
const frameCenterInRad = getUnformattedWCSPoint(frame.wcsInfo, frame.offsetCenter);
if (frame.isOffsetCoord && frameCenterInRad) {
frame.wcsInfoShifted = AST.createShiftmapFrameset(frame.wcsInfo, frameCenterInRad.x, frameCenterInRad.y);
frame.wcsInfoShifted = AST.createShiftmapFrameset(
frame.wcsInfo,
frameCenterInRad.x,
frameCenterInRad.y,
this.offsetCenter.x - frame.spatialTransform.translation.x,
this.offsetCenter.y - frame.spatialTransform.translation.y
);
}
}
}
Expand Down Expand Up @@ -2835,7 +2859,7 @@ export class FrameStore {
if (this.isOffsetCoord && !this.wcsInfoShifted) {
const centerInRad = getUnformattedWCSPoint(this.wcsInfo, this.center);
if (centerInRad) {
this.wcsInfoShifted = AST.createShiftmapFrameset(this.wcsInfo, centerInRad.x, centerInRad.y);
this.wcsInfoShifted = AST.createShiftmapFrameset(this.wcsInfo, centerInRad.x, centerInRad.y, this.offsetCenter.x, this.offsetCenter.y);
}
}

Expand Down
35 changes: 28 additions & 7 deletions src/stores/OverlayStore/OverlayStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as AST from "ast_wrapper";
import {action, autorun, computed, makeObservable, observable} from "mobx";

import {WCSType} from "models";
import {AppStore, PreferenceStore} from "stores";
import {AlertStore, AppStore, PreferenceStore} from "stores";
import {FrameStore, OverlayBeamStore, WCS_PRECISION} from "stores/Frame";
import {clamp, getColorForTheme, toFixed} from "utilities";

Expand Down Expand Up @@ -32,7 +32,8 @@ export enum SystemType {
FK4 = "FK4",
FK5 = "FK5",
Galactic = "GALACTIC",
ICRS = "ICRS"
ICRS = "ICRS",
Image = "CARTESIAN"
}

export enum NumberFormatType {
Expand Down Expand Up @@ -99,9 +100,12 @@ export class OverlayGlobalSettings {
astString.add("Labelling", this.labelType);
astString.add("Color", AstColorsIndex.GLOBAL);
astString.add("Tol", toFixed(this.tolerance / 100, 2), this.tolerance >= 0.001); // convert to fraction
astString.add("System", this.explicitSystem);
const isWcsFrameAndSystem = typeof this.explicitSystem !== "undefined" && this.explicitSystem !== SystemType.Image && frame.validWcs;
if (isWcsFrameAndSystem) {
astString.add("System", this.explicitSystem);
}

if ((frame?.isXY || frame?.isYX) && !frame?.isPVImage && typeof this.explicitSystem !== "undefined") {
if ((frame?.isXY || frame?.isYX) && !frame?.isPVImage && isWcsFrameAndSystem) {
if (this.system === SystemType.FK4) {
astString.add("Equinox", "1950");
} else {
Expand Down Expand Up @@ -149,8 +153,17 @@ export class OverlayGlobalSettings {
this.labelType = labelType;
}

@action setSystem(system: SystemType) {
this.system = system;
@action async setSystem(system: SystemType) {
const frames = AppStore.Instance.frames;
if ((this.system === SystemType.Image) !== (system === SystemType.Image) && frames.map(f => f.spatialReference !== null).includes(true)) {
const confirm = await AlertStore.Instance.showInteractiveAlert("Switching system between world and image coordinates will disable spatial matching.");
if (confirm) {
frames.forEach(f => f.clearSpatialReference());
this.system = system;
}
} else {
this.system = system;
}
}

@action setDefaultSystem(system: SystemType) {
Expand Down Expand Up @@ -1021,7 +1034,7 @@ export class OverlayStore {
autorun(() => {
this.setFormatsFromSystem();
AppStore.Instance.frames.forEach(frame => {
if (frame?.validWcs && frame?.wcsInfoForTransformation && this.global.explicitSystem) {
if (frame?.validWcs && frame?.wcsInfoForTransformation && this.global.explicitSystem && this.global.explicitSystem !== SystemType.Image) {
AST.set(frame.wcsInfoForTransformation, `System=${this.global.explicitSystem}`);
}
});
Expand Down Expand Up @@ -1244,4 +1257,12 @@ export class OverlayStore {
return renderHeight > 1 ? renderHeight : 1; // return value > 1 to prevent crashing
};
}

@computed get isWcsCoordinates() {
return this.global.explicitSystem !== SystemType.Image;
}

@computed get isImgCoordinates() {
return this.global.explicitSystem === SystemType.Image;
}
}
12 changes: 11 additions & 1 deletion src/utilities/wcs/wcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as AST from "ast_wrapper";
import {CARTA} from "carta-protobuf";

import {Point2D, SPECTRAL_DEFAULT_UNIT, SpectralType, WCSPoint2D} from "models";
import {NumberFormatType} from "stores";
import {NumberFormatType, OverlayStore} from "stores";
import {FrameStore} from "stores/Frame";
import {add2D, magDir2D, polygonPerimeter, rotate2D, scale2D, subtract2D, trimFitsComment} from "utilities";

Expand Down Expand Up @@ -77,10 +77,20 @@ export function getFormattedWCSPoint(astTransform: AST.FrameSet, pixelCoords: Po

export function getUnformattedWCSPoint(astTransform: AST.FrameSet, pixelCoords: Point2D) {
if (astTransform) {
if (OverlayStore.Instance.isImgCoordinates) {
// need second frame(WCS frame) in the frame to get WCS point
AST.setI(astTransform, "Current", 2);
}

const equinox = AST.getString(astTransform, "System") === "FK4" ? "1950.0" : "2000.0";
AST.set(astTransform, `Equinox=${equinox}`);
const pointWCS = transformPoint(astTransform, pixelCoords);
const normVals = AST.normalizeCoordinates(astTransform, pointWCS.x, pointWCS.y);

if (OverlayStore.Instance.isImgCoordinates) {
AST.setI(astTransform, "Current", 1);
}

if (normVals) {
return normVals;
}
Expand Down
Loading

0 comments on commit e22bf70

Please sign in to comment.