Exemplo n.º 1
0
    def set_compass(self, image):
        """Update the Compass plugin with info from the given image Data object."""
        if self.compass is None:  # Maybe another viewer has it
            return

        zoom_limits = (self.state.x_min, self.state.y_min, self.state.x_max,
                       self.state.y_max)
        if data_has_valid_wcs(image):
            wcs = image.coords

            # Convert X,Y from reference data to the one we are actually seeing.
            if self.get_link_type(image.label) == 'wcs':
                x = wcs.world_to_pixel(
                    self.state.reference_data.coords.pixel_to_world(
                        (self.state.x_min, self.state.x_max),
                        (self.state.y_min, self.state.y_max)))
                zoom_limits = (x[0][0], x[1][0], x[0][1], x[1][1])
        else:
            wcs = None

        arr = image[image.main_components[0]]
        vmin, vmax = PercentileInterval(95).get_limits(arr)
        norm = ImageNormalize(vmin=vmin, vmax=vmax, stretch=LinearStretch())
        self.compass.draw_compass(
            image.label,
            wcs_utils.draw_compass_mpl(arr,
                                       wcs,
                                       show=False,
                                       zoom_limits=zoom_limits,
                                       norm=norm))
Exemplo n.º 2
0
    def center_on(self, point):
        """Centers the view on a particular point.

        Parameters
        ----------
        point : tuple or `~astropy.coordinates.SkyCoord`
            If tuple of ``(X, Y)`` is given, it is assumed
            to be in data coordinates and 0-indexed.

        Raises
        ------
        AttributeError
            Sky coordinates are given but image does not have a valid WCS.

        """
        i_top = get_top_layer_index(self)
        image = self.layers[i_top].layer

        if isinstance(point, SkyCoord):
            if data_has_valid_wcs(image):
                try:
                    pix = image.coords.world_to_pixel(point)  # 0-indexed X, Y
                except NoConvergence as e:  # pragma: no cover
                    self.session.hub.broadcast(
                        SnackbarMessage(
                            f'{point} is likely out of bounds: {repr(e)}',
                            color="warning",
                            sender=self))
                    return
            else:
                raise AttributeError(
                    f'{getattr(image, "label", None)} does not have a valid WCS'
                )
        else:
            pix = point

        # Disallow centering outside of display; image.shape is (Y, X)
        eps = sys.float_info.epsilon
        if (not np.all(np.isfinite(pix)) or pix[0] < -eps or pix[0] >=
            (image.shape[1] + eps) or pix[1] < -eps or pix[1] >=
            (image.shape[0] + eps)):
            self.session.hub.broadcast(
                SnackbarMessage(f'{pix} is out of bounds',
                                color="warning",
                                sender=self))
            return

        width = self.state.x_max - self.state.x_min
        height = self.state.y_max - self.state.y_min

        with delay_callback(self.state, 'x_min', 'x_max', 'y_min', 'y_max'):
            self.state.x_min = pix[0] - (width * 0.5)
            self.state.y_min = pix[1] - (height * 0.5)
            self.state.x_max = self.state.x_min + width
            self.state.y_max = self.state.y_min + height
Exemplo n.º 3
0
    def on_mouse_or_key_event(self, data):

        # Find visible layers
        visible_layers = [
            layer for layer in self.state.layers if layer.visible
        ]

        if len(visible_layers) == 0:
            return

        if self.label_mouseover is None:
            if 'g-coords-info' in self.session.application._tools:
                self.label_mouseover = self.session.application._tools[
                    'g-coords-info']
            else:
                return

        if data['event'] == 'mousemove':
            # Display the current cursor coordinates (both pixel and world) as
            # well as data values. For now we use the first dataset in the
            # viewer for the data values.

            # Extract first dataset from visible layers and use this for coordinates - the choice
            # of dataset shouldn't matter if the datasets are linked correctly
            image = visible_layers[0].layer

            # Extract data coordinates - these are pixels in the image
            x = data['domain']['x']
            y = data['domain']['y']

            if x is None or y is None:  # Out of bounds
                self.label_mouseover.pixel = ""
                self.label_mouseover.reset_coords_display()
                self.label_mouseover.value = ""
                return

            maxsize = int(np.ceil(np.log10(np.max(image.shape)))) + 3
            fmt = 'x={0:0' + str(maxsize) + '.1f} y={1:0' + str(
                maxsize) + '.1f}'

            if data_has_valid_wcs(image):
                # Convert these to a SkyCoord via WCS - note that for other datasets
                # we aren't actually guaranteed to get a SkyCoord out, just for images
                # with valid celestial WCS
                try:
                    # Convert X,Y from reference data to the one we are actually seeing.
                    # world_to_pixel return scalar ndarray that we need to convert to float.
                    if self.get_link_type(image.label) == 'wcs':
                        x, y = list(
                            map(
                                float,
                                image.coords.world_to_pixel(
                                    self.state.reference_data.coords.
                                    pixel_to_world(x, y))))

                    self.label_mouseover.pixel = (fmt.format(x, y))
                    coo = image.coords.pixel_to_world(x, y).icrs
                    self.label_mouseover.set_coords(coo)
                except Exception:
                    self.label_mouseover.pixel = (fmt.format(x, y))
                    self.label_mouseover.reset_coords_display()
            else:
                self.label_mouseover.pixel = (fmt.format(x, y))
                self.label_mouseover.reset_coords_display()

            # Extract data values at this position.
            # TODO: for now we just use the first visible layer but we should think
            # of how to display values when multiple datasets are present.
            if (x > -0.5 and y > -0.5 and x < image.shape[1] - 0.5
                    and y < image.shape[0] - 0.5
                    and hasattr(visible_layers[0], 'attribute')):
                attribute = visible_layers[0].attribute
                value = image.get_data(attribute)[int(round(y)), int(round(x))]
                unit = image.get_component(attribute).units
                self.label_mouseover.value = f'{value:+10.5e} {unit}'
            else:
                self.label_mouseover.value = ''

        elif data['event'] == 'mouseleave' or data['event'] == 'mouseenter':

            self.label_mouseover.pixel = ""
            self.label_mouseover.reset_coords_display()
            self.label_mouseover.value = ""

        elif data['event'] == 'keydown' and data['key'] == 'b':
            self.blink_once()

            # Also update the coordinates display.
            data['event'] = 'mousemove'
            self.on_mouse_or_key_event(data)
Exemplo n.º 4
0
    def add_markers(self,
                    table,
                    x_colname='x',
                    y_colname='y',
                    skycoord_colname='coord',
                    use_skycoord=False,
                    marker_name=None):
        """Creates markers w.r.t. the reference image at given points
        in the table.

        .. note:: Use `marker` to change marker appearance.

        Parameters
        ----------
        table : `~astropy.table.Table`
            Table containing marker locations.

        x_colname, y_colname : str
            Column names for X and Y.
            Coordinates must be 0-indexed.

        skycoord_colname : str
            Column name with `~astropy.coordinates.SkyCoord` objects.

        use_skycoord : bool
            If `True`, use ``skycoord_colname`` to mark.
            Otherwise, use ``x_colname`` and ``y_colname``.

        marker_name : str, optional
            Name to assign the markers in the table. Providing a name
            allows markers to be removed by name at a later time.

        Raises
        ------
        AttributeError
            Sky coordinates are given but reference image does not have a valid WCS.

        ValueError
            Invalid marker name.

        """
        if marker_name is None:
            marker_name = self._default_mark_tag_name

        self._validate_marker_name(marker_name)
        jglue = self.session.application

        # Link markers to reference image data.
        image = self.state.reference_data

        # TODO: Is Glue smart enough to no-op if link already there?
        if use_skycoord:
            if not data_has_valid_wcs(image):
                raise AttributeError(
                    f'{getattr(image, "label", None)} does not have a valid WCS'
                )
            sky = table[skycoord_colname]
            t_glue = Data(marker_name, ra=sky.ra.deg, dec=sky.dec.deg)
            with jglue.data_collection.delay_link_manager_update():
                jglue.data_collection[marker_name] = t_glue
                jglue.add_link(t_glue, 'ra', image, 'Right Ascension')
                jglue.add_link(t_glue, 'dec', image, 'Declination')
        else:
            t_glue = Data(marker_name, **table[x_colname, y_colname])
            with jglue.data_collection.delay_link_manager_update():
                jglue.data_collection[marker_name] = t_glue
                jglue.add_link(t_glue, x_colname, image,
                               image.pixel_component_ids[1].label)
                jglue.add_link(t_glue, y_colname, image,
                               image.pixel_component_ids[0].label)

        try:
            self.add_data(t_glue)
        except Exception as e:  # pragma: no cover
            self.session.hub.broadcast(
                SnackbarMessage(
                    f"Failed to add markers '{marker_name}': {repr(e)}",
                    color="warning",
                    sender=self))
        else:
            # Only can set alpha and color using self.add_data(), so brute force here instead.
            # https://github.com/glue-viz/glue/issues/2201
            for key, val in self.marker.items():
                setattr(jglue.data_collection[
                    jglue.data_collection.labels.index(marker_name)].style,
                        key, val)  # noqa

            self._marktags.add(marker_name)
Exemplo n.º 5
0
    def offset_by(self, dx, dy):
        """Move the center to a point that is given offset
        away from the current center.

        Parameters
        ----------
        dx, dy : float or `~astropy.units.Quantity`
            Offset value. Without a unit, assumed to be pixel offsets.
            If a unit is attached, offset by pixel or sky is assumed from
            the unit.

        Raises
        ------
        AttributeError
            Sky offset is given but image does not have a valid WCS.

        ValueError
            Offsets are of different types.

        astropy.units.core.UnitTypeError
            Sky offset has invalid unit.

        """
        width = self.state.x_max - self.state.x_min
        height = self.state.y_max - self.state.y_min

        dx, dx_coord = _offset_is_pixel_or_sky(dx)
        dy, dy_coord = _offset_is_pixel_or_sky(dy)

        if dx_coord != dy_coord:
            raise ValueError(
                f'dx is of type {dx_coord} but dy is of type {dy_coord}')

        if dx_coord == 'wcs':
            i_top = get_top_layer_index(self)
            image = self.layers[i_top].layer
            if data_has_valid_wcs(image):
                # To avoid distortion headache, assume offset is relative to
                # displayed center.
                x_cen = self.state.x_min + (width * 0.5)
                y_cen = self.state.y_min + (height * 0.5)
                sky_cen = image.coords.pixel_to_world(x_cen, y_cen)
                if ASTROPY_LT_4_3:
                    from astropy.coordinates import SkyOffsetFrame
                    new_sky_cen = sky_cen.__class__(
                        SkyOffsetFrame(
                            dx, dy,
                            origin=sky_cen.frame).transform_to(sky_cen))
                else:
                    new_sky_cen = sky_cen.spherical_offsets_by(dx, dy)
                self.center_on(new_sky_cen)
            else:
                raise AttributeError(
                    f'{getattr(image, "label", None)} does not have a valid WCS'
                )
        else:
            with delay_callback(self.state, 'x_min', 'x_max', 'y_min',
                                'y_max'):
                self.state.x_min += dx
                self.state.y_min += dy
                self.state.x_max = self.state.x_min + width
                self.state.y_max = self.state.y_min + height