# Customizing the coordinate system of a data object¶

## Background¶

Data objects represented by the `Data`

class can have
a coordinate system defined, for display and/or linking purposes. This
coordinate system is defined in the `.coords`

attribute of data objects.
By default the `coords`

object for `Data`

objects
created manually is `None`

unless you explicitly specify `coords=`

when
creating the data object. For data objects returned by data loaders, whether
`coords`

is set or not will depend on the particular file format. For example
if we use the glue loaders to read in an example file:

```
>>> from glue.core.data_factories import load_data
>>> from glue.utils import require_data
>>> require_data('Astronomy/W5/w5.fits')
Successfully downloaded data file to w5.fits
>>> data = load_data('w5.fits')
```

the resulting `coords`

object has methods to convert between pixel coordinates
and so-called ‘world’ or ‘physical’ coordinates:

```
>>> data.coords.pixel_to_world_values(2, 3)
(array(46.34527244), array(58.85867558))
>>> data.coords.world_to_pixel_values(46.3, 58.9)
(array(10.39880029), array(16.44193896))
```

If not `None`

, the `coords`

attribute will be an object exposing the two
above methods as well as other useful methods and properties related to
coordinate transformations. The programmatic interface we have adopted for
`coords`

objects is described in A shared Python interface for World Coordinate Systems (while originally
defined by the Astropy project, this is very general and not astronomy-specific).
Any object implementing that API can be used as a coordinate object and will
integrate with the rest of glue.

A number of convenience coordinate classes are available in glue for common cases, and it is also possible to define your own (both options are described in the next sections).

## Affine coordinates¶

The most common cases of transformation between pixel and world coordinates are affine transformations, which can represent combinations of e.g. reflections, scaling, translations, rotations, and shear. A common way of representing an affine transformations is through an augmented matrix, which has shape N+1 x N+1, where N is the number of pixel and world dimensions.

Glue provides an `AffineCoordinates`

class for
representing arbitrary affine transformations:

```
>>> from glue.core.coordinates import AffineCoordinates
```

To initialize it, you will need to provide an augmented matrix, and optionally lists of units and axis names (as strings). For example, to construct an affine transformation where the x and y coordinates are each doubled, you would do:

```
>>> import numpy as np
>>> matrix = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 1]])
>>> affine_coords = AffineCoordinates(matrix, units=['m', 'm'], labels=['xw', 'yw'])
```

To use a custom coordinate system, when creating a data object you should specify
the coordinates object via the `coords=`

keyword argument:

```
>>> from glue.core import Data
>>> data_double = Data(x=[[1, 2], [3, 4]], coords=affine_coords)
>>> data_double.coords.pixel_to_world_values(2, 1)
(4.0, 2.0)
>>> data_double.coords.world_to_pixel_values(4.0, 2.0)
(2.0, 1.0)
```

## Identity coordinates¶

A special/simple case of coordinate transformation is the identity transform,
where world coordinates are the same as pixel coordinates. Glue provides an
`IdentityCoordinates`

class for representing
this transformation:

```
>>> from glue.core.coordinates import IdentityCoordinates
```

To initialize it, you will need to specify the number of dimensions in the data:

```
>>> data_ident = Data(x=[1, 2, 3], coords=IdentityCoordinates(n_dim=1))
>>> data_ident.coords.pixel_to_world_values(2, 1)
(2, 1)
>>> data_ident.coords.world_to_pixel_values(4.0, 2.0)
(4.0, 2.0)
```

## Custom coordinates¶

If you want to define a fully customized coordinate transformation, we
provide a `Coordinates`

class that you can
start from to make things easier. The only required methods in this case
are the following:

```
from glue.core.coordinates import Coordinates
class MyCoordinates(Coordinates):
def pixel_to_world_values(self, *args):
# This should take N arguments (where N is the number of dimensions
# in your dataset) and assume these are 0-based pixel coordinates,
# then return N world coordinates with the same shape as the input.
def world_to_pixel_values(self, *args):
# This should take N arguments (where N is the number of dimensions
# in your dataset) and assume these are 0-based pixel coordinates,
# then return N world coordinates with the same shape as the input.
```

In addition, you can also optionally specify units and names for all world coordinates with the two following properties:

```
@property
def world_axis_units(self):
# Returns an iterable of strings given the units of the world
# coordinates for each axis.
@property
def world_axis_names(self):
# Returns an iterable of strings given the names of the world
# coordinates for each axis.
```

For example, let’s consider a coordinate system where the world coordinates are simply scaled by a factor of two compared to the pixel coordinates. The minimal class implementing this would look like:

```
>>> from glue.core.coordinates import Coordinates
>>> class DoubleCoordinates(Coordinates):
...
... def pixel_to_world_values(self, *args):
... return tuple([2.0 * x for x in args])
...
... def world_to_pixel_values(self, *args):
... return tuple([0.5 * x for x in args])
```

To use a custom coordinate system, when creating a data object you should specify
the coordinates object via the `coords=`

keyword argument:

```
>>> data_double = Data(x=[1, 2, 3], coords=DoubleCoordinates(n_dim=1))
>>> data_double.coords.pixel_to_world_values(2)
(4.0,)
>>> data_double.coords.world_to_pixel_values(4.0)
(2.0,)
```

Note that the `n_dim=`

argument needs to be passed to give the number of
dimensions in the data.

In fact you do not need to start from our `Coordinates`

class - any class that conforms to the API described in
A shared Python interface for World Coordinate Systems is valid. If
you want full control over your coordinate transformations, we recomment you
take a look at that document.