1

As an example, in chaco/examples/demo/basic/image_inspector.py, how to set the zoom factor such that 1 array point corresponds to 1 screen pixel (100% zoom). It seems that the ZoomTool methods (zoom_in, zoom_out, ...) only deal with zoom factor changes, not with absolute factor setting.

Yves Surrel
  • 193
  • 1
  • 10

2 Answers2

1

I would try something with plot.range2d.low, plot.range2d.high and plot.outer_bounds. The first two relate to data space, while the latter relates to the size of the picture area. By setting the limits of the data space using the picture area, you can map 1 pixel to 1 data unit. Here's an example, the interesting bit is in the _zoom_100_percent method:

import numpy as np
from chaco.api import Plot, ArrayPlotData
from chaco.tools.api import PanTool, ZoomTool
from enable.api import ComponentEditor
from traits.api import Button, HasTraits, Instance, on_trait_change
from traitsui.api import Item, View

class HundredPercentZoom(HasTraits):
    plot = Instance(Plot)
    zoom_button = Button('100% Zoom')

    traits_view = View(
        Item('plot', editor=ComponentEditor(), show_label=False),
        'zoom_button',
        width=800,
        height=600,
    )

    def _plot_default(self):
        t = np.linspace(0, 1000, 200)
        y = 400 * (np.sin(t) + 0.1 * np.sin(t * 100))
        plot = Plot(ArrayPlotData(t=t, y=y))
        plot.plot(('t', 'y'))
        plot.tools.append(PanTool(plot))
        plot.tools.append(ZoomTool(plot))
        return plot

    @on_trait_change('zoom_button')
    def _zoom_100_percent(self):
        low = self.plot.range2d.low
        bounds = self.plot.outer_bounds
        print(bounds)
        self.plot.range2d.high = (low[0] + bounds[0], low[1] + bounds[1])

if __name__ == "__main__":
    hpz = HundredPercentZoom()
    hpz.configure_traits()

I added a print statement in there so you can see that the plot area is different than the window area, which is 800x600. I also added a PanTool and ZoomTool, so you can pan around once zoomed in. You can go back to the orignal zoom state using the Escape key, as long as your plot has a ZoomTool.

0

The solution I have arrived to, starting from the original example image_inspector.py . A button allows to have a 100 % zoom factor around a point chosen as the zoom center.

All is in the _btn_fired method in class Demo.

There may still be a problem of 1 not being subtracted or added to some bounds or limits, as the button operation is not strictly involutive (a second press should not do anything) as it should.

Anything simpler?

#!/usr/bin/env python
"""
Demonstrates the ImageInspectorTool and overlay on a colormapped image
plot.  The underlying plot is similar to the one in cmap_image_plot.py.
 - Left-drag pans the plot.
 - Mousewheel up and down zooms the plot in and out.
 - Pressing "z" brings up the Zoom Box, and you can click-drag a rectangular
   region to zoom.  If you use a sequence of zoom boxes, pressing alt-left-arrow
   and alt-right-arrow moves you forwards and backwards through the "zoom
   history".
 - Pressing "p" will toggle the display of the image inspector overlay.
"""

# Major library imports
from numpy import linspace, meshgrid, pi, sin, divide, multiply

# Enthought library imports
from enable.api import Component, ComponentEditor
from traits.api import HasTraits, Instance, Button, Float
from traitsui.api import Item, Group, View, HGroup

# Chaco imports
from chaco.api import ArrayPlotData, jet, Plot
from chaco.tools.api import PanTool, ZoomTool
from chaco.tools.image_inspector_tool import ImageInspectorTool, \
     ImageInspectorOverlay

#===============================================================================
# # Create the Chaco plot.
#===============================================================================
def _create_plot_component():# Create a scalar field to colormap
    xbounds = (-2*pi, 2*pi, 600)
    ybounds = (-1.5*pi, 1.5*pi, 300)
    xs = linspace(*xbounds)
    ys = linspace(*ybounds)
    x, y = meshgrid(xs,ys)
    z = sin(x)*y

    # Create a plot data obect and give it this data
    pd = ArrayPlotData()
    pd.set_data("imagedata", z)

    # Create the plot
    plot = Plot(pd)
    img_plot = plot.img_plot("imagedata",
                             xbounds = xbounds[:2],
                             ybounds = ybounds[:2],
                             colormap=jet)[0]

    # Tweak some of the plot properties
    plot.title = "My First Image Plot"
    plot.padding = 50

    # Attach some tools to the plot
    plot.tools.append(PanTool(plot))
    zoom = ZoomTool(component=plot, tool_mode="box", always_on=False)
    plot.overlays.append(zoom)
    imgtool = ImageInspectorTool(img_plot)
    img_plot.tools.append(imgtool)
    overlay = ImageInspectorOverlay(component=img_plot, image_inspector=imgtool,
                                    bgcolor="white", border_visible=True)

    img_plot.overlays.append(overlay)
    return plot

#===============================================================================
# Attributes to use for the plot view.
size = (800, 600)
title="Inspecting a Colormapped Image Plot"

#===============================================================================
# # Demo class that is used by the demo.py application.
#===============================================================================
class Demo(HasTraits):
    plot = Instance(Component)
    center_x = Float
    center_y = Float
    btn = Button('100 %')

    def _btn_fired(self):
        img_plot, = self.plot.plots['plot0']
        zoom_center = (self.center_x, self.center_y)
        # Size of plot in screen pixels
        plot_size = img_plot.bounds
        # Zoom center in screen space
        zoom_center_screen, = img_plot.map_screen(zoom_center)
        # Get actual bounds in data space
        low, high = (img_plot.index_mapper.range.low, 
            img_plot.index_mapper.range.high)
        # Get data space x and y units in terms of x and y array indices
        sizes = [item.get_size() for item in img_plot.index.get_data()]
        (min_x, min_y), (max_x, max_y) = img_plot.index.get_bounds()
        unit = divide((max_x - min_x, max_y - min_y), sizes)
        # Calculate new bounds
        new_low = zoom_center - multiply(zoom_center_screen, unit)
        new_high = new_low + multiply(plot_size, unit)
        # Set new bounds
        img_plot.index_mapper.range.set_bounds(new_low,new_high)

    traits_view = View(
                    Group(
                        Item('plot', editor=ComponentEditor(size=size),
                             show_label=False),
                        HGroup('center_x', 'center_y', 'btn'),
                        orientation = "vertical"
                    ),
                    resizable=True, title=title
                    )

    def _plot_default(self):
         return _create_plot_component()

demo = Demo()

if __name__ == "__main__":
    demo.configure_traits()
Yves Surrel
  • 193
  • 1
  • 10