Print the number of steps read in by plot_image.py (for sanity checks).
[parallel_computing.git] / src / plot_image / plot_image.py
index 4854eeadfd9aef2ce52a095ce18f36d1fb323fe9..11a3a9787b4a8b0aadf2b6718570a28b951008ff 100755 (executable)
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/env python
 
 """Generate an image from ASCII data.
 
@@ -15,7 +15,7 @@ Data should be one ASCII float per line, in the following order::
     z[x1,y1]
     z[x2,y1]
     ...
-    z[nN,y1]
+    z[xN,y1]
     z[x1,y2]
     ...
     z[xN,y2]
@@ -25,7 +25,19 @@ Data should be one ASCII float per line, in the following order::
 where `x` increases from `x1` to `xN` and `y` increases from `y1`
 through `yN`.
 
-For  usage info, run::
+You can use the `--xyz` option to read an alternative data format,
+which is::
+
+    x1 y1 z[x1,y1]
+    x2 y2 z[x1,y1]
+    ...
+
+If you use this method, the ordering of lines in the data file is
+irrelevant, and `Nx` and `Ny` are extracted from the data file itself.
+However, you still need to use a rectangular grid (i.e. for every
+`xi`, you need to have entries for every `yi`).
+
+For usage info, run::
 
     ./plot_image.py --help
 
@@ -42,11 +54,12 @@ For other ideas, see the Matplotlib website [2]_.
 import optparse
 import sys
 
+import matplotlib
+import matplotlib.image
 import numpy
 
 # Depending on your Matplotlib configuration, you may need to adjust
 # your backend.  Do this before importing pylab or matplotlib.backends.
-#import matplotlib
 #matplotlib.use('Agg')     # select backend that doesn't require X Windows
 #matplotlib.use('GTKAgg')  # select backend that supports pylab.show()
 
@@ -56,12 +69,12 @@ import pylab
 _DOC = __doc__
 
 
-def read_data(stream, nx, ny):
+def read_data_1d(stream, nx, ny):
     """Read in data, one entry per line.
 
     >>> from StringIO import StringIO
-    >>> s = StringIO('\\n'.join([str(x) for x in range(10)])+'\\n')
-    >>> X,Y,Z = read_data(s, 5, 2)
+    >>> s = StringIO('\\n'.join(map(str, range(10)))+'\\n')
+    >>> X,Y,Z = read_data_1d(s, 5, 2)
     >>> X
     array([[0, 1, 2, 3, 4, 5],
            [0, 1, 2, 3, 4, 5],
@@ -81,8 +94,49 @@ def read_data(stream, nx, ny):
     Z = Z.reshape([x-1 for x in X.shape])    
     return (X,Y,Z)
 
+def read_data_3d(stream):
+    """Read in data, one `(x, y, z)` tuple per line.
+
+    >>> from StringIO import StringIO
+    >>> lines = []
+    >>> for x in range(5):
+    ...     for y in range(2):
+    ...         lines.append('\t'.join(map(str, [x, y, x+y*5])))
+    >>> s = StringIO('\\n'.join(lines)+'\\n')
+    >>> X,Y,Z = read_data_3d(s)
+    >>> X
+    array([[ 0.,  1.,  2.,  3.,  4.,  5.],
+           [ 0.,  1.,  2.,  3.,  4.,  5.],
+           [ 0.,  1.,  2.,  3.,  4.,  5.]])
+    >>> Y
+    array([[ 0.,  0.,  0.,  0.,  0.,  0.],
+           [ 1.,  1.,  1.,  1.,  1.,  1.],
+           [ 2.,  2.,  2.,  2.,  2.,  2.]])
+    >>> Z
+    array([[ 0.,  1.,  2.,  3.,  4.],
+           [ 5.,  6.,  7.,  8.,  9.]])
+    """
+    XYZ = numpy.loadtxt(stream)
+    assert len(XYZ.shape) == 2 and XYZ.shape[1] == 3, XYZ.shape
+    Xs = numpy.array(sorted(set(XYZ[:,0])))
+    Ys = numpy.array(sorted(set(XYZ[:,1])))
+    Z = numpy.ndarray((len(Ys), len(Xs)), dtype=float)
+    xyz = {}  # dict of z values keyed by (x,y) tuples
+    for i in range(XYZ.shape[0]):
+        xyz[(XYZ[i,0], XYZ[i,1])] = XYZ[i,2]
+    for i,x in enumerate(Xs):
+        for j,y in enumerate(Ys):
+            Z[j,i] = xyz[x,y]
+    # add dummy row/column for pcolor
+    dx = Xs[-1] - Xs[-2]
+    dy = Ys[-1] - Ys[-2]
+    Xs = numpy.append(Xs, Xs[-1] + dx)
+    Ys = numpy.append(Ys, Ys[-1] + dy)
+    X,Y = pylab.meshgrid(Xs, Ys)
+    return (X,Y,Z)
 
-def plot(X, Y, Z, full=False, title=None, contours=None, cmap=None):
+def plot(X, Y, Z, full=False, title=None, contours=None, interpolation=None,
+         cmap=None):
     """Plot Z over the mesh X, Y.
 
     >>> X, Y = pylab.meshgrid(range(6), range(2))
@@ -97,9 +151,9 @@ def plot(X, Y, Z, full=False, title=None, contours=None, cmap=None):
 
     fig = pylab.figure()
     if full:
-        axes = pylab.axes([0, 0, 1, 1])
+        axes = fig.add_axes([0, 0, 1, 1])
     else:
-        axes = pylab.axes()
+        axes = fig.add_subplot(1, 1, 1)
         if title:
             axes.set_title(title)
     axes.set_axis_off()
@@ -107,12 +161,12 @@ def plot(X, Y, Z, full=False, title=None, contours=None, cmap=None):
     if contours:
         cset = axes.contour(X[:-1,:-1], Y[:-1,:-1], Z, contours, cmap=cmap)
         # [:-1,:-1] to strip dummy last row & column from X&Y.
-        pylab.clabel(cset, inline=1, fmt='%1.1f', fontsize=10)
+        axes.clabel(cset, inline=1, fmt='%1.1f', fontsize=10)
     else:
         # pcolor() is much slower than imshow.
         #plot = axes.pcolor(X, Y, Z, cmap=cmap, edgecolors='none')
         #axes.autoscale_view(tight=True)
-        plot = axes.imshow(Z, aspect='auto', interpolation='bilinear',
+        plot = axes.imshow(Z, aspect='auto', interpolation=interpolation,
                            origin='lower', cmap=cmap,
                            extent=(X_min, X_max, Y_min, Y_max))
         if not full:
@@ -120,6 +174,17 @@ def plot(X, Y, Z, full=False, title=None, contours=None, cmap=None):
     return fig
 
 
+def get_possible_interpolations():
+    try:  # Matplotlib v1.0.1
+        return sorted(matplotlib.image.AxesImage._interpd.keys())
+    except AttributeError:
+        try:  # Matplotlib v0.91.2
+            return sorted(matplotlib.image.AxesImage(None)._interpd.keys())
+        except AttributeError:
+            # give up ;)
+            pass
+    return ['nearest']
+
 def test():
     import doctest
     results = doctest.testmod()
@@ -139,7 +204,9 @@ def main(argv=None):
     Title:             Some like it hot
     Image size:        5 2
     False color
-    Data range:        0.0 9.0
+    X range:           0 4 (6 steps)
+    Y range:           0 1 (3 steps)
+    Z range:           0.0 9.0
     >>> img = o.read()
     >>> img.startswith('\\x89PNG')
     True
@@ -161,6 +228,10 @@ def main(argv=None):
                        'displaying it immediately'))
     p.add_option('-s', '--size', dest='size', default='%d,%d' % (16, 16),
                  help='Data size (columns,rows; default: %default)')
+    p.add_option('-3', '--xyz', dest='xyz', default=False, action='store_true',
+                 help=('If set, read (x,y,z) tuples from the input data rather'
+                       'then reading `z` and calculating `x` and `y` from '
+                       '`--size`.'))
     p.add_option('-c', '--contours', dest='contours', type='int',
                  help=('Number of contour lines (if not set, draw false color '
                        'instead of contour lines; default: %default)'))
@@ -171,6 +242,10 @@ def main(argv=None):
                  help='Title (%default)')
     p.add_option('--test', dest='test', action='store_true',
                  help='Run internal tests and exit.')
+    interpolations = get_possible_interpolations()
+    p.add_option('--interpolation', dest='interpolation', default='nearest',
+                 help=('Interpolation scheme (for false color images) from %s '
+                       '(%%default)') % ', '.join(interpolations))
     maps=[m for m in pylab.cm.datad if not m.endswith("_r")]
     maps.sort()
     p.add_option('-m', '--color-map', dest='cmap', default='jet',
@@ -190,7 +265,8 @@ def main(argv=None):
 
     print 'Plot_image'
     print 'Title:            ', options.title
-    print 'Image size:       ', nx, ny
+    if not options.xyz:
+        print 'Image size:       ', nx, ny
     if options.contours:
         print '# countour lines: ', options.contours
     else:
@@ -200,22 +276,29 @@ def main(argv=None):
     else:
         fin = sys.stdin
 
-    X,Y,Z = read_data(fin, nx, ny)
+    if options.xyz:
+        X,Y,Z = read_data_3d(fin)
+    else:
+        X,Y,Z = read_data_1d(fin, nx, ny)
 
     if options.input:
         fin.close()
 
     Z_min = numpy.min(Z.flat)
     Z_max = numpy.max(Z.flat)
-    print 'Data range:       ', Z_min, Z_max
+    print 'X range:           {} {} ({} steps)'.format(
+        X[0,0], X[0,-2], X.shape[1])
+    print 'Y range:           {} {} ({} steps)'.format(
+        Y[0,0], Y[-2,0], Y.shape[0])
+    print 'Z range:          ', Z_min, Z_max
 
     fig = plot(X, Y, Z, full=options.full, title=options.title,
-               contours=options.contours, cmap=cmap)
+               contours=options.contours, interpolation=options.interpolation,
+               cmap=cmap)
 
     if options.output:
         fig.savefig(options.output)
     else:
-        pylab.ion()
         pylab.show()