3

I have the following data points that represent the boundaries of a constellation.

let boundaries = [
    {
      ra: 344.46530375,
      dec: 35.1682358
    },
    {
      ra: 344.34285125,
      dec: 53.1680298
    },
    {
      ra: 351.45289375,
      dec: 53.1870041
    },
    ...
]

Irrelevant, but these have been extracted from IAU quoted values.

However. Between each of these points I need what is known as the "great circle distance" - or the orthodromic distance, between point i and point i+1. To do that, we can interpolate between point i and point i+1 at a given precision, x.

I have written the following function which can perform this operation (it's worth noting that is done in Equatorial Coordinates - not Cartesian - so the Mathematics is slightly more complex, but not massively so).

The function for interpolate between two points is as follows (written in Typescript ES6 for prosperity and safety):

/**
 * getInterpolatedEquatorialCoordinates()
 *
 * @description This function takes in a start and end
 * @param start EquatorialCoordinate
 * @param end EquatorialCoordinate
 * @param precision number
 * @output returns the interpolated array of []EquatorialCoordinate points:
 */
export const getInterpolatedEquatorialCoordinates = (
  start: EquatorialCoordinate,
  end: EquatorialCoordinate,
  precision: number
): Array<EquatorialCoordinate> => {
  // The interpolated points array to return:
  const points: Array<EquatorialCoordinate> = []

  // Obtain the difference between the end coordinate's Right Ascension, RA, and the start coordinate's RA:
  let nra = ((end.ra - start.ra)) % 360

  if (nra > 180) nra = nra - 360
  if (nra < -180) nra = nra + 360

  // Obtain the "gradient" rate of change of RA:
  const dra: number = nra / precision

  // Obtain the difference between the end coordinate's Declination, dec, and the start coordinate's dec:
  const ndec = ((end.dec - start.dec))

  // Obtain the "gradient" rate of change of dec:
  const ddec: number = ndec / precision

  let i = 0

  // Obtain the interpolated EquatorialCoordinate points for each step:
  while (i < precision) {
    i++
    points.push({
      ra: start.ra + (dra * i),
      dec: start.dec + (ddec * i)
    })
  }

  return points
}

I have run this through a test suit, between the first two points of the above boundaries, to obtain it's validity Mathematically:

test('getInterpolatedEquatorialCoordinates', function () {
  const start = {
    ra: 344.46530375,
    dec: 35.1682358
  }

  const end = {
    ra: 344.34285125,
    dec: 53.1680298
  }

  const minRA = Math.min(start.ra, end.ra)
  const minDec = Math.min(start.dec, end.dec)

  const maxRA = Math.max(start.ra, end.ra)
  const maxDec = Math.max(start.ra, end.ra)

  const precision = 10

  const points = getInterpolatedEquatorialCoordinates(start, end, precision)

  const isRAAboveLowerThreshold = (
    p: EquatorialCoordinate
  ) => p.ra >= minRA

  const isRABelowUpperThreshold = (
    p: EquatorialCoordinate
  ) => p.ra <= maxRA

  const isDecAboveLowerThreshold = (
    p: EquatorialCoordinate
  ) => p.dec >= minDec

  const isDecBelowUpperThreshold = (
    p: EquatorialCoordinate
  ) => p.dec <= maxDec

  expect(points.length).toBeGreaterThan(0)
  expect(points.length).toBe(precision)
  expect(points.every(isRAAboveLowerThreshold)).toBe(true)
  expect(points.every(isRABelowUpperThreshold)).toBe(true)
  expect(points.every(isDecAboveLowerThreshold)).toBe(true)
  expect(points.every(isDecBelowUpperThreshold)).toBe(true)
})

The interpolated outputted points are as follows:

[
      { ra: 344.4530585, dec: 36.968215199999996 },
      { ra: 344.44081324999996, dec: 38.7681946 },
      { ra: 344.428568, dec: 40.568174 },
      { ra: 344.41632275, dec: 42.3681534 },
      { ra: 344.40407749999997, dec: 44.168132799999995 },
      { ra: 344.39183225, dec: 45.9681122 },
      { ra: 344.379587, dec: 47.7680916 },
      { ra: 344.36734175000004, dec: 49.568071 },
      { ra: 344.3550965, dec: 51.3680504 },
      { ra: 344.34285125, dec: 53.1680298 }
]

This seems to work for any precision to a good degree of accuracy that I am happy with, it also works regardless of which "direction" we're going.


Question:

What I want to do now is to apply this to the above boundaries array, for every point, returning a new array of all of the points combined as one ...

What would be the best way to achieve this? I've tried using the Array.prototype.map method, but to zero success ...

The desired output is as follows for the following "boundaries":

let boundaries = [
    {
      ra: 344.46530375,
      dec: 35.1682358
    },
    {
      ra: 344.34285125,
      dec: 53.1680298
    },
    ...
]

let desiredOutput = [
  {
    ra: 344.46530375,
    dec: 35.1682358
  }, // Original Index 0
  // Start Interpolated Intermediary Values
  { 
    ra: 344.4530585, 
    dec: 36.968215199999996 
  },
  { 
    ra: 344.44081324999996,    
    dec: 38.7681946 
  },
  { 
    ra: 344.428568, 
    dec: 40.568174 
  },
  { 
    ra: 344.41632275, 
    dec: 42.3681534 
  },
  { 
    ra: 344.40407749999997, 
    dec: 44.168132799999995 
  },
  { 
    ra: 344.39183225, 
    dec: 45.9681122 
  },
  { 
    ra: 344.379587, 
    dec: 47.7680916 
  },
  { 
    ra: 344.36734175000004, 
    dec: 49.568071 
  },
  { 
    ra: 344.3550965, 
    dec: 51.3680504 
  },
  { 
    ra: 344.34285125, 
    dec: 53.1680298 
  },
  // End Interpolated Intermediary Values
  {
    ra: 344.34285125,
    dec: 53.1680298
  }, // Original Index 1
  ...
]
7
  • If I understand this, your boundaries array has multiple right ascension/declination pairs but your interpolation function expects start and end co-ordinates, each of which represents an ra/dec pair. How do you decide what the start and end pairs should be when invoking the interpolation function? Is it simply adjacent pairs in the boundaries list? So [0,1], then [1,2], then [2,3], up to [N-1,0]? Commented Aug 18, 2021 at 14:41
  • @jarmod Exactly that - unfortunately, the IAU simply provide the boundary points as a "continuous" line of points. I understand why you asked this tho, as it might be a random ordered list of points. Commented Aug 18, 2021 at 14:44
  • Each invocation of the interpolation function returns an array of P points (where P=precision, 10 in the example). If there are K points in the boundaries array, what does your final, desired output look like? Is it a 1-D array of K*P points? Commented Aug 18, 2021 at 15:29
  • Yeh, a 1-D array but of K(1+P) points (original points remain and remain in order - interpolated between). I'll add the desired output to the above question also. Commented Aug 18, 2021 at 15:30
  • @Jarmod I've added the desired output. Let me know if this helps. Commented Aug 18, 2021 at 15:35

2 Answers 2

2

I think Array.reduce() is the best tool to use here.

You could write a function that takes an array of boundaries and a preceistion and outputs your desired array of EquatorialCoordinate:

const fillInBoundryPoints = (boundaries: EquatorialCoordinate[], precision: number) => {
  return boundaries.reduce((acc, cur, i, arr) => {
    if (i < arr.length - 1) {
      acc.push(cur)
      acc.push(...getInterpolatedEquatorialCoordinates(cur, arr[i + 1], precision))
    }
    return acc
  }, [] as EquatorialCoordinate[])
}

Which you would use as so:

const precision = 10

const boundaries: EquatorialCoordinate[] = [
  {
    ra: 344.46530375,
    dec: 35.1682358
  },
  {
    ra: 344.34285125,
    dec: 53.1680298
  }
]

const desiredOutput = fillInBoundryPoints(boundaries, precision)

Outputs:

[
  { ra: 344.46530375, dec: 35.1682358 },
  { ra: 344.4530585, dec: 36.968215199999996 },
  { ra: 344.44081324999996, dec: 38.7681946 },
  { ra: 344.428568, dec: 40.568174 },
  { ra: 344.41632275, dec: 42.3681534 },
  { ra: 344.40407749999997, dec: 44.168132799999995 },
  { ra: 344.39183225, dec: 45.9681122 },
  { ra: 344.379587, dec: 47.7680916 },
  { ra: 344.36734175000004, dec: 49.568071 },
  { ra: 344.3550965, dec: 51.3680504 },
  { ra: 344.34285125, dec: 53.1680298 }
]
Sign up to request clarification or add additional context in comments.

Comments

2

You could use the map but, the reduce is a more natural fit for this scenario

boundaries.reduce((a,p,i,arr) => {
  if ( i > 0 ) {
    a.push(getInterpolatedEquatorialCoordinates(arr[i-1], p, precision))
  }
  return a;
}, [])

NOTE: If you are using the map case instead of looking up the previous value you have to lookahead but, you will end up with an undefined at the end which you need to slice off

5 Comments

Hmmmm, this seems to be returning an empty array and a lot of typescript issues as well. I will tag this question as Typescript. Really appreciate your answer nonetheless.
getInterpolatedEquatorialCoordinates() returns an Array as well FYI, so we can't push ...
You can push an array onto an array, and the accumulator for this is [].
Yes, of course - technically you can, but then we just get an array inside an array - which is not what the desiredOutput is.
So just flatten it with a.push(...getInterpolatedEquatorialCoordinates(arr[i-1], p, precision))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.