Skip to content

Commit

Permalink
Merge pull request #404 from observerly/feature/wcs/convertPixelToWor…
Browse files Browse the repository at this point in the history
…ldCoordinateSystem

feat: add convertPixelToWorldCoordinateSystem() to wcs module in @observerly/astrometry
  • Loading branch information
michealroberts authored Nov 12, 2024
2 parents 12bfe37 + 5f6c7d5 commit 65c91a3
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 0 deletions.
75 changes: 75 additions & 0 deletions src/wcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,80 @@ export type WCS = {
SIP?: SIP2DParameters
}

/*****************************************************************************************************************/

/**
*
* convertPixelToWorldCoordinateSystem
*
* Converts a given image pixel to an equatorial coordinate base on the prescribed WCS
*
* @param wcs the World Coordinate System (WCS) parameters to use for the conversion
* @param pixel the pixel to convert to an equatorial coordinate, e.g., { x: 0, y: 0 }
* @returns the equatorial coordinate of a given pixel in the prescribed WCS
*/
export const convertPixelToWorldCoordinateSystem = (
wcs: WCS,
pixel: CartesianCoordinate
): EquatorialCoordinate => {
// Calculate the pixel delta in the X dimension relative to central reference pixel:
let deltaX = pixel.x - wcs.crpix.x

// Calculate the pixel delta in the Y dimension relative to central reference pixel:
let deltaY = pixel.y - wcs.crpix.y

// Initialize SIP correction terms for A and B:
let A = 0
let B = 0

// Apply SIP polynomial corrections if SIP parameters are defined
if (wcs.SIP) {
// Apply A polynomial corrections:
for (const term in wcs.SIP.APower) {
const coeff = wcs.SIP.APower[term]
const indices = parseSIPTerm(term, 'A')
if (indices) {
const [i, j] = indices
A += coeff * deltaX ** i * deltaY ** j
}
}

// Apply B polynomial corrections:
for (const term in wcs.SIP.BPower) {
const coeff = wcs.SIP.BPower[term]
const indices = parseSIPTerm(term, 'B')
if (indices) {
const [i, j] = indices
B += coeff * deltaX ** i * deltaY ** j
}
}
}

// Apply forward SIP transformation to correct for non-linear distortions:
deltaX += A
deltaY += B

// Calculate the reference equatorial coordinate for the right ascension:
let ra = wcs.cd11 * deltaX + wcs.cd12 * deltaY + wcs.E

// Correct for large values of RA:
ra = ra % 360

// Correct for negative values of RA:
if (ra < 0) {
ra += 360
}

// Calculate the reference equatorial coordinate for the declination:
let dec = wcs.cd21 * deltaX + wcs.cd22 * deltaY + wcs.F

// Correct for large values of DEC:
dec = dec % 90

return {
ra,
dec
}
}

/*****************************************************************************************************************/
72 changes: 72 additions & 0 deletions tests/wcs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*****************************************************************************************************************/

// @author Michael Roberts <[email protected]>
// @package @observerly/astrometry/wcs
// @license Copyright © 2021-2023 observerly

/*****************************************************************************************************************/

import { describe, expect, it } from 'vitest'

import {
type SIP2DParameters,
WCS,
convertPixelToWorldCoordinateSystem
} from '../src'

/*****************************************************************************************************************/

describe('convertPixelToWorldCoordinateSystem', () => {
it('should be defined', () => {
expect(convertPixelToWorldCoordinateSystem).toBeDefined()
})

it('should convert pixel coordinates to equatorial coordinates correctly without SIP transformations', () => {
const wcs = {
crpix: { x: 200, y: 200 },
crval: { ra: 0, dec: 0 },
cd11: 0.2,
cd12: 30,
cd21: 0.2,
cd22: 0.2,
E: 0,
F: 0
} satisfies WCS

const pixel = { x: 100, y: 100 }

const equatorial = convertPixelToWorldCoordinateSystem(wcs, pixel)

expect(equatorial).toEqual({ ra: 220, dec: -40 })
})

it('should convert pixel coordinates to equatorial coordinates correctly with SIP transformations', () => {
const sip = {
AOrder: 2,
BOrder: 2,
APower: { A_1_0: 0, A_0_1: 0, A_1_1: 2.47110429124e-07, A_2_0: 6.71868472834e-07, A_0_2: 2.8223469986e-07 },
BPower: { B_1_0: 0, B_0_1: 0, B_1_1: 2.47110429124e-07, B_2_0: 6.71868472834e-07, B_0_2: 2.8223469986e-07 }
} satisfies SIP2DParameters

const wcs = {
crpix: { x: 200, y: 200 },
crval: { ra: 0, dec: 0 },
cd11: 0.2,
cd12: 30,
cd21: 0.2,
cd22: 0.2,
E: 0,
F: 0,
SIP: sip
} satisfies WCS

const pixel = { x: 100, y: 100 }

const equatorial = convertPixelToWorldCoordinateSystem(wcs, pixel)

expect(equatorial.ra).toBeCloseTo(220.36, 1)
expect(equatorial.dec).toBeCloseTo(-39.99, 1)
})
})

/*****************************************************************************************************************/

0 comments on commit 65c91a3

Please sign in to comment.