Add clickloc post and bring in plotpick.py.
authorW. Trevor King <wking@drexel.edu>
Thu, 18 Nov 2010 14:35:14 +0000 (09:35 -0500)
committerW. Trevor King <wking@drexel.edu>
Thu, 18 Nov 2010 14:35:14 +0000 (09:35 -0500)
posts/clicklock.mdwn [new file with mode: 0644]
posts/clicklock/clickloc.tk [new file with mode: 0644]
posts/clicklock/scale_click.sh [new file with mode: 0644]
posts/plotpick.mdwn [moved from posts/PlotPick_program.mdwn with 64% similarity]
posts/plotpick/plotpick.py [new file with mode: 0644]

diff --git a/posts/clicklock.mdwn b/posts/clicklock.mdwn
new file mode 100644 (file)
index 0000000..1383f38
--- /dev/null
@@ -0,0 +1,22 @@
+[[!meta  title="Image click locations"]]
+
+The [[clickloc.tk]] micro-app just opens an image file and prints out
+the pixel coordinates of any mouse clicks upon it. I use it to get
+rough numbers from journal figures. You can use [[scale_click.sh]] to
+convert the output to units of your choice, if you use your first four
+clicks to mark out the coordinate system (xmin,anything),
+(xmax,anything), (anything,ymin), and (anything,ymax).
+
+    $ pdfimages article.pdf fig
+    $ clickloc.tk fig-000.ppm > fig-000.pixels
+    $ scale_click.sh 0 10 5 20 fig-000.pixels > fig-000.data
+
+Take a look at [[plotpick]] for grabbing points from raw datafiles
+(which is more accurate and easier than reverse engineering images).
+
+[[!tag tags/bash]]
+[[!tag tags/code]]
+[[!tag tags/linux]]
+[[!tag tags/programming]]
+[[!tag tags/tcl-tk]]
+[[!tag tags/theory]]
diff --git a/posts/clicklock/clickloc.tk b/posts/clicklock/clickloc.tk
new file mode 100644 (file)
index 0000000..f8ec6a7
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/wish
+#
+# from http://wiki.tcl.tk/876
+# http://www.tcl.tk/man/tcl8.4/TkCmd/canvas.htm
+# http://wiki.tcl.tk/1623
+#
+# Open an image and record the pixel coordinates of mouseclicks on it.
+# Useful for extracting rough numbers from journal figures.
+#
+# usage: clickloc.tk imagefile
+
+# The image package lets you open more image formats.
+# You can invoke this package with either of the following lines
+#load /usr/local/lib/Img1.2/libimg1.2.so
+#package require Img
+
+if { $argc != 1 } {
+    puts "usage: clickloc.tk imagefile"
+    exit 1
+}
+
+set imagefile $argv
+
+set picture [image create photo -file $imagefile]
+set height [image height $picture]
+set width [image width $picture]
+canvas .can -height $height -width $width
+.can create image 0 0 -image $picture -anchor nw -tag img
+pack .can
+
+.can bind img <Button-1> {
+    puts "%x\t%y"
+}
diff --git a/posts/clicklock/scale_click.sh b/posts/clicklock/scale_click.sh
new file mode 100644 (file)
index 0000000..ee51550
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+if [ $# -ne 5 ]
+then
+    echo "usage: scale_click.sh xmin xmax ymin ymax file"
+    exit 1
+fi
+
+# reference points in the final units from the command line
+XMIN=$1
+XMAX=$2
+YMIN=$3
+YMAX=$4
+FILE=$5
+
+# reference points in pixels from the first 4 clicks
+XMINP=`sed -n '1p' $FILE | cut -f1`
+XMAXP=`sed -n '2p' $FILE | cut -f1`
+YMINP=`sed -n '3p' $FILE | cut -f2`
+YMAXP=`sed -n '4p' $FILE | cut -f2`
+
+echo "# Xp -> ($XMAX-$XMIN)/($XMAXP-$XMINP)*(Xp-$XMINP) + $XMIN"
+echo "# Yp -> ($YMAX-$YMIN)/($YMAXP-$YMINP)*(Yp-$YMINP) + $YMIN"
+
+awk "{if(NR > 4){print ($XMAX-$XMIN)/($XMAXP-$XMINP)*(\$1-$XMINP) + $XMIN, \
+                       ($YMAX-$YMIN)/($YMAXP-$YMINP)*(\$2-$YMINP) + $YMIN}}" \
+    $FILE
+
+exit 0
similarity index 64%
rename from posts/PlotPick_program.mdwn
rename to posts/plotpick.mdwn
index 015b9a61261df06d66671d22a29be48099745bd7..581d04807b3f77fe8f697373f7bfb6605ee8e5de 100644 (file)
@@ -6,12 +6,11 @@ set boundaries programmatically at the moment, I thought I'd write up
 a little utility to record clicks on key data points, so I could pick
 out the “good data”.
 
-Enter
-[plotpick.py](http://www.physics.drexel.edu/~wking/code/index.shtml#plotpick),
-the raw-data version of
-[clicklock.tk](http://www.physics.drexel.edu/~wking/blog/archives/2008/09/05/index.html#e2008-09-05T20_20_30.txt).
-Hope you like it :).
+Enter [[plotpick.py]] the raw-data version of [[clicklock]].  Hope you
+like it :).
 
+[[!tag tags/code]]
 [[!tag tags/linux]]
 [[!tag tags/programming]]
+[[!tag tags/python]]
 [[!tag tags/theory]]
diff --git a/posts/plotpick/plotpick.py b/posts/plotpick/plotpick.py
new file mode 100644 (file)
index 0000000..8564b71
--- /dev/null
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+"""
+Pick data-points with a cursor.  The cursor snaps to the nearest
+datapoint.  Developed from Matplotlib's cursor_demo.py.
+
+Faster cursoring is possible using native GUI drawing, as in
+wxcursor_demo.py.  You could also write up a more efficient
+Cursor._get_xy() method.
+"""
+
+from pylab import subplot, connect, show, draw
+from numpy import array, fromfile, double
+
+class Cursor:
+    """
+    Like Cursor but the crosshair snaps to the nearest x,y point
+    For simplicity, I'm assuming x is sorted
+    """
+    def __init__(self, ax, x, y):
+        self.ax = ax
+        self.lx, = ax.plot( (0,0), (0,0), 'k-' )  # the horiz line
+        self.ly, = ax.plot( (0,0), (0,0), 'k-' )  # the vert line
+        self.x = x
+        self.y = y
+        self.xscale = max(self.x) - min(self.x)
+        self.yscale = max(self.y) - min(self.y)
+        self._sort()
+        # text location in axes coords
+        #self.txt = ax.text( 0.6, 0.9, '', transform=ax.transAxes)
+        self.txt = self.ax.title
+    def _sort(self):
+        "ideally, here is where you build the Voronoi lookup tree"
+        pass
+    def _get_xy(self, x, y):
+        """terrible hack.  Should compute Voronoi diagram with
+        some sort of lookup tree.  Work for my free time...
+        http://en.wikipedia.org/wiki/Voronoi_diagram
+        http://en.wikipedia.org/wiki/Point_location#Triangulation_refinement
+        http://www.cs.cmu.edu/~quake/triangle.html
+        """
+        dist = float("infinity")
+        indx = -1
+        xs = self.xscale
+        ys = self.yscale
+        for xp,yp,i in zip(self.x, self.y,range(len(self.x))):
+            d = (((x-xp)/xs)**2 + ((y-yp)/ys)**2)**0.5
+            if d < dist:
+                dist = d
+                xpm = xp
+                ypm = yp
+                indx = i
+        return (xpm,ypm,indx)
+    def mouse_move(self, event):
+        if not event.inaxes: return
+        ax = event.inaxes
+        minx, maxx = ax.get_xlim()
+        miny, maxy = ax.get_ylim()
+        x, y, i = self._get_xy(event.xdata, event.ydata)
+        # update the line positions
+        self.lx.set_data( (minx, maxx), (y, y) )
+        self.ly.set_data( (x, x), (miny, maxy) )
+        # update the label
+        self.txt.set_text( 'x=%1.2g, y=%1.2g, indx=%d'%(x,y,i) )
+        draw()
+    def mouse_click(self, event):
+        if not event.inaxes: return
+        x, y, i = self._get_xy(event.xdata, event.ydata)
+        if event.button != 3: return # ignore non-button-3 clicks
+        print '%g\t%g\t%d'%(x,y,i)
+
+def readFile(fid, cols=2, sep='\t'):
+    """This is the lower level reader.  It works on all file types, but
+    you need to know the number of columns in the file ahead of time."""
+    data = fromfile(file=fid, dtype=double, sep=sep)
+    rows = len(data)/cols
+    data = data.reshape((rows,cols))
+    x = data[:,0]
+    y = data[:,1]
+    return x,y
+
+def readFilename(filename, sep='\t'):
+    """Requires rewinding file if first line is not a comment.
+    Therefore only useful on
+      * seekable files (e.g. read from disk)
+      * FIFOs/piped-data with headers"""
+    fid = file(filename,'r')
+    headline = fid.readline()
+    if headline[0] != "#": fid.seek(0) # rewind if headline was data
+    cols = headline.count(sep)+1
+    x,y = readFile(fid, cols=cols, sep=sep)
+    fid.close()
+    return x,y
+
+if __name__ == "__main__" :
+    import sys
+
+    if len(sys.argv) > 2 :
+        print "usage: plotpick.py [datafile]"
+        print """
+Reads in ASCII data from datafile, or, if datafile is not given, from
+stdin.  Data files define an array of (x,y) points and consist of two
+columns (x&y) seperated by TABs, with ENDLINEs between the points:
+x0     y0
+x1     y1
+...
+The points are plotted in a window, and mousing over the plot displays
+a cursor that snaps to the nearest point.  Right-clicking (button 3)
+will print the TAB seperated coordinates (x, y, point-index) to stdout.
+"""
+        sys.exit(1)
+    elif len(sys.argv) == 1:
+        x,y = readFile(sys.stdin)
+    else: # len(sys.argv) == 2
+        datafile = file(sys.argv[1], 'r')
+        x,y =  readFile(datafile)
+        datafile.close
+
+    ax = subplot(111)    
+    cursor = Cursor(ax, x, y)
+    connect('motion_notify_event', cursor.mouse_move)
+    connect('button_press_event', cursor.mouse_click)    
+    ax.plot(x, y, '.')
+    ax.axis([min(x), max(x), min(y), max(y)])
+
+    print '#x\ty\tindex'
+    show()