or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

c-extensions.mdcontext-analysis.mdcoordinate-utils.mdindex.mdresampling.md

coordinate-utils.mddocs/

0

# Coordinate Transformation Utilities

1

2

Tools for calculating pixel mappings between different coordinate systems, estimating pixel scale ratios, and working with World Coordinate System (WCS) objects. These utilities support the coordinate transformations needed for drizzling operations.

3

4

```python { .api }

5

from typing import Optional, Union, List, Tuple

6

import numpy as np

7

```

8

9

## Capabilities

10

11

### Pixel Map Calculation

12

13

Calculate discretized coordinate mappings between two images using their World Coordinate System (WCS) objects.

14

15

```python { .api }

16

def calc_pixmap(

17

wcs_from,

18

wcs_to,

19

shape: Optional[Tuple[int, int]] = None,

20

disable_bbox: str = "to"

21

) -> np.ndarray:

22

"""

23

Calculate pixel-to-pixel mapping between two coordinate systems.

24

25

Transforms pixel coordinates from the 'from' image to pixel coordinates

26

in the 'to' image using their respective WCS objects.

27

28

Parameters:

29

- wcs_from: WCS object for source coordinate system

30

Must have array_shape property or bounding_box if shape not provided

31

- wcs_to: WCS object for destination coordinate system

32

- shape: Output array shape (Ny, Nx) in numpy order

33

If None, uses wcs_from.array_shape or derives from bounding_box

34

- disable_bbox: Controls bounding box usage ("to", "from", "both", "none")

35

"to" disables bounding box for destination WCS

36

"from" disables bounding box for source WCS

37

"both" disables both, "none" uses both

38

39

Returns:

40

3D numpy array of shape (Ny, Nx, 2) where:

41

- [..., 0] contains X-coordinates in destination system

42

- [..., 1] contains Y-coordinates in destination system

43

44

Raises:

45

- ValueError: If output shape cannot be determined from inputs

46

47

Notes:

48

- Output frames of both WCS objects must have same units

49

- Coordinates outside bounding boxes may be set to NaN depending on disable_bbox

50

- Used to generate pixmap arrays for Drizzle.add_image()

51

"""

52

```

53

54

### Pixel Scale Ratio Estimation

55

56

Estimate the ratio of pixel scales between two WCS objects at specified reference positions.

57

58

```python { .api }

59

def estimate_pixel_scale_ratio(

60

wcs_from,

61

wcs_to,

62

refpix_from: Optional[Union[np.ndarray, Tuple, List]] = None,

63

refpix_to: Optional[Union[np.ndarray, Tuple, List]] = None

64

) -> float:

65

"""

66

Compute ratio of pixel scales between two WCS objects.

67

68

Pixel scale is estimated as square root of pixel area (assumes square pixels).

69

If reference pixel positions are not provided, uses center of bounding box,

70

center of pixel_shape, or (0, 0) as fallback.

71

72

Parameters:

73

- wcs_from: Source WCS object (must have pixel_shape property)

74

- wcs_to: Destination WCS object

75

- refpix_from: Reference pixel coordinates in source image for scale calculation

76

Can be numpy array, tuple, or list of coordinates

77

- refpix_to: Reference pixel coordinates in destination image for scale calculation

78

Can be numpy array, tuple, or list of coordinates

79

80

Returns:

81

Float representing ratio of destination to source pixel scales

82

(pixel_scale_to / pixel_scale_from)

83

84

Notes:

85

- Useful for determining scale parameter in drizzling operations

86

- Reference pixels default to image centers when not specified

87

- Uses approximate algorithm for efficiency

88

"""

89

```

90

91

## Usage Examples

92

93

### Basic Pixel Mapping

94

95

```python

96

import numpy as np

97

from drizzle.utils import calc_pixmap

98

from astropy import wcs

99

100

# Create sample WCS objects (in real usage, these come from FITS headers)

101

# Here we simulate simple WCS objects

102

class SimpleWCS:

103

def __init__(self, pixel_shape, scale=1.0, offset=(0, 0)):

104

self.array_shape = pixel_shape

105

self.scale = scale

106

self.offset = offset

107

108

def pixel_to_world_values(self, x, y):

109

return x * self.scale + self.offset[0], y * self.scale + self.offset[1]

110

111

def world_to_pixel_values(self, world_x, world_y):

112

return (world_x - self.offset[0]) / self.scale, (world_y - self.offset[1]) / self.scale

113

114

# Source image: 100x100 pixels

115

wcs_from = SimpleWCS(pixel_shape=(100, 100), scale=1.0, offset=(0, 0))

116

117

# Destination: 150x150 pixels with different scale and offset

118

wcs_to = SimpleWCS(pixel_shape=(150, 150), scale=0.8, offset=(10, 5))

119

120

# Calculate pixel mapping

121

pixmap = calc_pixmap(wcs_from, wcs_to)

122

123

print(f"Pixel map shape: {pixmap.shape}") # (100, 100, 2)

124

print(f"Sample mapping - pixel (50,50) maps to: ({pixmap[50,50,0]:.2f}, {pixmap[50,50,1]:.2f})")

125

```

126

127

### Custom Shape and Bounding Box Control

128

129

```python

130

from drizzle.utils import calc_pixmap

131

132

# Calculate mapping with custom output shape

133

pixmap = calc_pixmap(

134

wcs_from,

135

wcs_to,

136

shape=(200, 180), # Custom shape instead of using wcs_from.array_shape

137

disable_bbox="both" # Disable both bounding boxes

138

)

139

140

print(f"Custom shape mapping: {pixmap.shape}")

141

142

# Different bounding box configurations

143

pixmap_from_disabled = calc_pixmap(wcs_from, wcs_to, disable_bbox="from")

144

pixmap_to_disabled = calc_pixmap(wcs_from, wcs_to, disable_bbox="to") # Default

145

pixmap_none_disabled = calc_pixmap(wcs_from, wcs_to, disable_bbox="none")

146

```

147

148

### Pixel Scale Ratio Calculation

149

150

```python

151

import numpy as np

152

from drizzle.utils import estimate_pixel_scale_ratio

153

154

# Calculate pixel scale ratio between two coordinate systems

155

ratio = estimate_pixel_scale_ratio(wcs_from, wcs_to)

156

print(f"Pixel scale ratio (to/from): {ratio:.4f}")

157

158

# Calculate ratio at specific reference points

159

ref_from = (25, 25) # Reference point in source image

160

ref_to = (30, 28) # Reference point in destination image

161

162

ratio_at_refs = estimate_pixel_scale_ratio(

163

wcs_from,

164

wcs_to,

165

refpix_from=ref_from,

166

refpix_to=ref_to

167

)

168

print(f"Ratio at reference points: {ratio_at_refs:.4f}")

169

170

# Using numpy arrays for reference points

171

ref_from_array = np.array([25.5, 25.5])

172

ref_to_array = np.array([30.2, 28.1])

173

174

ratio_arrays = estimate_pixel_scale_ratio(

175

wcs_from,

176

wcs_to,

177

refpix_from=ref_from_array,

178

refpix_to=ref_to_array

179

)

180

print(f"Ratio with array inputs: {ratio_arrays:.4f}")

181

```

182

183

### Integration with Drizzling

184

185

```python

186

import numpy as np

187

from drizzle.resample import Drizzle

188

from drizzle.utils import calc_pixmap, estimate_pixel_scale_ratio

189

190

# Setup coordinate systems

191

wcs_input = SimpleWCS(pixel_shape=(120, 120), scale=1.0)

192

wcs_output = SimpleWCS(pixel_shape=(200, 200), scale=0.75)

193

194

# Calculate transformations

195

pixmap = calc_pixmap(wcs_input, wcs_output)

196

scale_ratio = estimate_pixel_scale_ratio(wcs_input, wcs_output)

197

198

# Create sample data

199

data = np.random.random((120, 120)).astype(np.float32)

200

201

# Use calculated parameters in drizzling

202

drizzler = Drizzle(out_shape=(200, 200))

203

nmiss, nskip = drizzler.add_image(

204

data=data,

205

exptime=10.0,

206

pixmap=pixmap,

207

scale=scale_ratio # Use calculated scale ratio

208

)

209

210

print(f"Drizzling completed: missed={nmiss}, skipped={nskip}")

211

```

212

213

### Working with Real Astropy WCS

214

215

```python

216

# Example with actual astropy WCS objects (requires astropy)

217

try:

218

from astropy import wcs

219

from astropy.io import fits

220

221

# Load WCS from FITS headers (example)

222

# header1 = fits.getheader('input_image.fits')

223

# header2 = fits.getheader('output_image.fits')

224

# wcs1 = wcs.WCS(header1)

225

# wcs2 = wcs.WCS(header2)

226

227

# For demonstration, create simple WCS

228

w1 = wcs.WCS(naxis=2)

229

w1.wcs.crpix = [50, 50]

230

w1.wcs.cdelt = [0.1, 0.1]

231

w1.wcs.crval = [0, 0]

232

w1.array_shape = (100, 100)

233

234

w2 = wcs.WCS(naxis=2)

235

w2.wcs.crpix = [75, 75]

236

w2.wcs.cdelt = [0.08, 0.08]

237

w2.wcs.crval = [1, 1]

238

w2.array_shape = (150, 150)

239

240

# Calculate pixel mapping

241

pixmap = calc_pixmap(w1, w2)

242

scale_ratio = estimate_pixel_scale_ratio(w1, w2)

243

244

print(f"WCS pixel map shape: {pixmap.shape}")

245

print(f"Scale ratio: {scale_ratio:.4f}")

246

247

except ImportError:

248

print("Astropy not available for this example")

249

```

250

251

## Error Handling

252

253

```python

254

from drizzle.utils import calc_pixmap, estimate_pixel_scale_ratio

255

256

# Handle cases where shape cannot be determined

257

class BadWCS:

258

def __init__(self):

259

self.array_shape = None

260

# No bounding_box property either

261

262

try:

263

bad_wcs = BadWCS()

264

good_wcs = SimpleWCS((100, 100))

265

pixmap = calc_pixmap(bad_wcs, good_wcs) # No shape info available

266

except ValueError as e:

267

print(f"Shape determination error: {e}")

268

269

# Handle coordinate transformation errors

270

try:

271

# WCS objects that don't implement required methods

272

class IncompleteWCS:

273

array_shape = (50, 50)

274

# Missing required transformation methods

275

276

incomplete_wcs = IncompleteWCS()

277

pixmap = calc_pixmap(incomplete_wcs, good_wcs)

278

except AttributeError as e:

279

print(f"WCS method error: {e}")

280

```

281

282

## Notes

283

284

- These utilities are designed to work with WCS objects that implement the standard transformation methods (`pixel_to_world_values`, `world_to_pixel_values`)

285

- The `calc_pixmap` function is the primary way to generate coordinate mappings for drizzling operations

286

- Pixel scale ratios help determine appropriate scaling parameters for flux conservation

287

- Bounding box handling allows control over coordinate transformations at image edges

288

- All coordinate arrays use numpy's float64 precision for accuracy in astronomical coordinate transformations