#
# coordmap.py -- coordinate mappings.
#
# This is open-source software licensed under a BSD license.
# Please see the file LICENSE.txt for details.
#
import numpy as np
from ginga import trcalc
from ginga.util import wcs
__all__ = ['NativeMapper', 'WindowMapper', 'CartesianMapper',
'DataMapper', 'OffsetMapper', 'WCSMapper']
class CoordMapError(Exception):
pass
class BaseMapper(object):
"""Base class for coordinate mapper objects."""
def __init__(self):
super(BaseMapper, self).__init__()
def to_canvas(self, canvas_x, canvas_y):
raise CoordMapError("this method is deprecated")
def to_data(self, pts):
raise CoordMapError("subclass should override this method")
def data_to(self, pts):
raise CoordMapError("subclass should override this method")
def offset_pt(self, pts, offset):
"""
Offset a point specified by `pt`, by the offsets `offset`.
Coordinates are assumed to be in the space defined by this mapper.
"""
raise CoordMapError("subclass should override this method")
def rotate_pt(self, pts, theta, offset=None):
"""
Rotate a point specified by `pt` by the angle `theta` (in degrees)
around the point indicated by `offset`.
Coordinates are assumed to be in the space defined by this mapper.
"""
raise CoordMapError("subclass should override this method")
[docs]class NativeMapper(BaseMapper):
"""A coordinate mapper that maps to the viewer's canvas in
the viewer's canvas coordinates.
"""
def __init__(self, viewer):
super(NativeMapper, self).__init__()
self.viewer = viewer
[docs] def to_data(self, cvs_pts, viewer=None):
if viewer is None:
viewer = self.viewer
cvs_arr = np.asarray(cvs_pts)
return viewer.tform['data_to_native'].from_(cvs_arr)
[docs] def data_to(self, data_pts, viewer=None):
if viewer is None:
viewer = self.viewer
data_arr = np.asarray(data_pts)
return viewer.tform['data_to_native'].to_(data_arr)
[docs] def offset_pt(self, pts, offset):
return np.add(pts, offset)
[docs] def rotate_pt(self, pts, theta, offset):
# TODO? Not sure if it is needed with this mapper type
return pts
[docs]class WindowMapper(BaseMapper):
"""A coordinate mapper that maps to the viewer in 'window' coordinates.
"""
def __init__(self, viewer):
super(WindowMapper, self).__init__()
self.viewer = viewer
[docs] def to_data(self, cvs_pts, viewer=None):
if viewer is None:
viewer = self.viewer
cvs_arr = np.asarray(cvs_pts)
return viewer.tform['data_to_window'].from_(cvs_arr)
[docs] def data_to(self, data_pts, viewer=None):
if viewer is None:
viewer = self.viewer
data_arr = np.asarray(data_pts)
return viewer.tform['data_to_window'].to_(data_arr)
[docs] def offset_pt(self, pts, offset):
return np.add(pts, offset)
[docs] def rotate_pt(self, pts, theta, offset):
# TODO? Not sure if it is needed with this mapper type
return pts
[docs]class CartesianMapper(BaseMapper):
"""A coordinate mapper that maps to the viewer in Cartesian
coordinates that do not scale (unlike DataMapper).
"""
def __init__(self, viewer):
super(CartesianMapper, self).__init__()
self.viewer = viewer
[docs] def to_data(self, crt_pts, viewer=None):
if viewer is None:
viewer = self.viewer
crt_arr = np.asarray(crt_pts)
return viewer.tform['data_to_cartesian'].from_(crt_arr)
[docs] def data_to(self, data_pts, viewer=None):
if viewer is None:
viewer = self.viewer
data_arr = np.asarray(data_pts)
return viewer.tform['data_to_cartesian'].to_(data_arr)
[docs] def offset_pt(self, pts, offset):
return np.add(pts, offset)
[docs] def rotate_pt(self, pts, theta, offset):
x, y = np.asarray(pts).T
xoff, yoff = np.transpose(offset)
rot_x, rot_y = trcalc.rotate_pt(x, y, theta, xoff=xoff, yoff=yoff)
return np.asarray((rot_x, rot_y)).T
[docs]class DataMapper(BaseMapper):
"""A coordinate mapper that maps to the viewer in data coordinates.
"""
def __init__(self, viewer):
super(DataMapper, self).__init__()
self.viewer = viewer
[docs] def to_data(self, data_pts, viewer=None):
return data_pts
[docs] def data_to(self, data_pts, viewer=None):
return data_pts
[docs] def offset_pt(self, pts, offset):
return np.add(pts, offset)
[docs] def rotate_pt(self, pts, theta, offset):
x, y = np.asarray(pts).T
xoff, yoff = np.transpose(offset)
rot_x, rot_y = trcalc.rotate_pt(x, y, theta, xoff=xoff, yoff=yoff)
return np.asarray((rot_x, rot_y)).T
[docs]class OffsetMapper(BaseMapper):
"""A coordinate mapper that maps to the viewer in data coordinates
that are offsets relative to some other reference object.
"""
def __init__(self, viewer, refobj):
super(OffsetMapper, self).__init__()
# TODO: provide a keyword arg to specify which point in the obj
self.viewer = viewer
self.refobj = refobj
[docs] def calc_offsets(self, pts):
ref_pt = self.refobj.get_reference_pt()
return np.subtract(pts, ref_pt)
[docs] def to_data(self, delta_pt, viewer=None):
if viewer is None:
viewer = self.viewer
ref_pt = self.refobj.get_reference_pt()
data_pt = self.refobj.crdmap.to_data(ref_pt, viewer=viewer)
return np.add(data_pt, delta_pt)
[docs] def data_to(self, data_pts, viewer=None):
ref_pt = self.refobj.get_reference_pt()
return np.subtract(data_pts, ref_pt)
[docs] def offset_pt(self, pts, offset):
# A no-op because this object's points are always considered
# relative to the reference object
return pts
[docs] def rotate_pt(self, pts, theta, offset):
# TODO? Not sure if it is needed with this mapper type
return pts
[docs]class WCSMapper(BaseMapper):
"""A coordinate mapper that maps to the viewer in WCS coordinates.
"""
def __init__(self, viewer):
super(WCSMapper, self).__init__()
self.viewer = viewer
[docs] def to_data(self, wcs_pts, viewer=None):
if viewer is None:
viewer = self.viewer
return viewer.tform['wcs_to_data'].to_(wcs_pts)
[docs] def data_to(self, data_pts, viewer=None):
if viewer is None:
viewer = self.viewer
data_arr = np.asarray(data_pts)
return viewer.tform['wcs_to_data'].from_(data_arr)
[docs] def offset_pt(self, pts, offset):
x, y = np.transpose(pts)
xoff, yoff = np.transpose(offset)
res_arr = wcs.add_offset_radec(x, y, xoff, yoff)
return np.transpose(res_arr)
[docs] def rotate_pt(self, pts, theta, offset):
# TODO: rotate in WCS space?
# rotate in data space
data_off = self.to_data(offset)
data_pts = self.to_data(pts)
xoff, yoff = np.transpose(data_off)
data_x, data_y = data_pts.T
data_rot = trcalc.rotate_pt(data_x, data_y, theta,
xoff=xoff, yoff=yoff)
return self.data_to(data_rot.T)
# END