GraphReader
Purpose:Simple and effective data reader of digitized graphs in formats
like .xbm, .gif, .jpg, assuming linear coordination (essentially all graphs are
linear for your eyes - the only difference is in later interpretation). For
anyone who can use a scanner, say good-bye to the terrible work of taking down
data from journals using a pencil and a ruler!
Instructions:
- The program uses both JavaScript and Java, and they need to be enabled in
the browser. There are no other system requirements.
- To map a linear graph, it suffices to set three anchors. I.e.,
you tell the program what x,y the current pointer corresponds to.
Usually an anchor could be the origin or either end of a graph's axes. To set
anchors,
- Click on any point in the graph. You see a dot indicating the pointer.
- Use Up,Down,Left,Right arrow keys to
precisely move the pointer to desired position (Ctrl or Shift
accelerates its motion). Ctrl/Shift with mouse clicks will
also do.
- Press Return to highlight the text field at bottom. Type in
actual x,y values separated by space.
- Press Return or click the Set 1st anchor button to
submit the data. Same for the 2nd and 3rd anchors. If you further click the
button, you mean to reset all three anchors.
The three anchors should not be degenerate, i.e., they should
not be all on a straight line. Also, it is crucial to tell the program their
accurate x,y values. If the program considers the three anchors "sick," it
will ask the user to restart from scratch by showing Set 1st anchor
button immediately.
- After the anchors are set, you can see the pointer x,y values in the
status bar. Press s to save this data.
- Press d to delete a saved data that is nearest to the pointer.
- Press c to clear all saved data.
- Check show saved box to see your saved data on graph.
- You can dump your saved data to Java console by clicking buttons
MLB (Matlab raw data), LTX (LaTeX table), or HTML
(table). That can then be pasted to your text editor. To turn on Java console,
- Netscape Communicator 4.0-4.05: Communicator->Java Console.
- Netscape Communicator 4.5: Communicator->Tools->Java
Console.
- Microsoft Internet Explorer 4.x: if you have not enabled Java console in
MSIE, you need to do this first: View->Internet
Options->Advanced->Java VM->Java Console Enabled, then restart
MSIE (you only need to do it once). Then, to toggle Java console:
View->Java Console.
Notes:
- V1.0 (JDK 1.0.2) coded in 1997 for my fiancee. Only Netscape on Sun
workstations. Unstable on other platforms. The current version is in JDK
1.1.3, tested on i386 RedHat Linux Netscape 4.61, SGI IRIX Netscape 4.07,
Microsoft Internet Explorer 4.x.
- The pathological interaction between keyboard events intercepted by
handleEvent(), and applet top management and input TextField
in V1.0, is now resolved by adding a KeyEvent receptor (pseudo-TextField or
applet this) that gets all command keys, a bit like two modes of
vi. The switch is the return key.
- It suffices to define just two anchors if the graph is orthogonal and
"sitting up." But the scanned image is often slightly rotated, and I don't
want this error to exist in the program. To benchmark V2.0's precision, try test.gif
generated by test.m.
A conservative estimate is 1% relative error for reasonable images. And the
larger the image is, the higher the precision.
- To pick your color for the pointer, refer to rgb.html.
My favorite is SteelBlue4. The original width and
height of an image can be determined by loading the raw
image into the browser, right click on it, and look for Properties or
View Page Info. Use magn factor (default=1.0) to enlarge or
shrink your graph. If the image is larger than the browser, pressing
Up,Down,Left,Right arrow keys when the mouse
cursor is outside the applet will force the browser to show scrollbars. You
can always regain the focus for KeyEvents by clicking in the graph.
Background color of the graph is taken to be white but that can be changed.
To make sure that the pointer works for transparent gifs like trans.gif,
I cover the floor with this color before drawing the real image on it, and the
pointer color is what one gets when drawing on that color. In case
your image is not transparent and the majority of its dots are red instead of
white, then the program should be informed that the background is FF
00 00, in order to gain your desired pointer color when drawing on red.
- It is against security
restrictions to let a Java applet sent by an "untrusted" server - running
now on the client browser, to read or write the local file system. It is also
against security restrictions to have third-party communication other than
between the browser and the server. Thus, embedable Java applets are mostly
"blind performers" who need
little viewer information. The only viable method is to let reader.htm,
reader.class
be on the same computer as the digitized graphs. There are, of course, two
ways to do this.
- The programmer writes paint(g), but the applet may pass graphics
contexts other than getGraphics() - the whole context you see on the
screen - to paint. When an overlapping window is pushed down, the
applet may ask paint to draw on a "different" and smaller g,
then transfer (copy or simply "on"?) to getGraphics(). Watch the
action of a slow IE after we insert a line g=getGraphics(); at the
beginning of paint(g) - in this case the entire graph is redrawn
every time, whereas previously it only draws in the overlap region, which is
more efficient.
The priority of applet is speed and memory. It does not care about your
discomfort with screen flickering. In first download and repaint() -
g is getGraphics() - and the applet
will directly draw on getGraphics() through
update(g) to paint(g), unless you override
update(g). Double-buffering decreases frame rate but is visually more
appealing. One draws on bak_g, then copies to (instead of draws on)
g. Management would never do that voluntarily.
Once we see that g may not be getGraphics(), confusions
about setXORMode are cleared. Basically, if the current context color
a=g.getColor() [which can be set by g.setColor(a), or to be
the initialized black], after which g.setXORMode(b), then if we draw
thereafter on g's dot of color c: c ->
a (+) b (+) c. Thus, if a is picked or
made to be the "background color" of g, where the majority of
g's dots have c=a, then it would turn b after we
draw on the background once. On other dots (like a yellow circle in mostly
white background), the result is unpredictable, but always reversible before
setXORMode is changed. The point is, it is important to be sure that
a=g.getColor() is what you think it is before
running g.setXORMode(b), otherwise you won't get the planned effects
- like getting b when drawing on the background of g.
http://mmm.mit.edu/~liju99/Archive/NetApp/JavaScript/Reader/reader.htm
stores the newest version of Graph Reader. Java source reader.java
is freely available on condition that I am notified of its hacks and upgrades.
The author is a follower of the Open Source movement.
Use this JavaScript:
<SCRIPT>
function init()
{
// Default applet geometries
var graph_width_pad=10, graph_height_pad=10, panel_height=40;
// Graph's pathname to be passed to the applet: if it is local,
// there must not be "file://" tag - just "c:\A\b.gif". If it
// is remote, it should be like "http://a.b.com/b.gif".
var graph_file = "";
// The graph's pathname could be browsed in (local), in which
// case it already satisfies the required format "c:\A\b.gif".
graph_file = document.myform.graph_file.value;
// Or could be typed in, interpreted as relative to this HTML
// base, and we need to add it back in. However if the document
// base contains "file://", we need to throw that part out.
if (graph_file.length == 0) // no input from Browse
graph_file = document.URL.substring(0,
document.URL.indexOf("reader.htm")).replace("/\./", "/").
replace("file://", "").replace("file:", "")
+ document.myform.graph_filename.value;
// Intended display geometry (width height magnification):
var graph_geom_splitted = document.myform.graph_geom.value.split(" ");
var graph_width = graph_geom_splitted[0];
var graph_height = graph_geom_splitted[1];
var graph_mag = 1.0;
if (graph_geom_splitted.length >= 3)
graph_mag = parseFloat (graph_geom_splitted[2]);
// convert string to integer by multiplication
graph_width = Math.round(graph_mag * graph_width);
graph_height = Math.round(graph_mag * graph_height);
var applet_width = 2 * graph_width_pad + graph_width;
var applet_height = 2 * graph_height_pad + panel_height + graph_height;
// The pointer is a square rotated by 90 degrees
var pointer_splitted = document.myform.pointer.value.split(" ");
var pointer_size = pointer_splitted[0];
var pointer_R = parseInt(pointer_splitted[1],16)/255.;
var pointer_G = parseInt(pointer_splitted[2],16)/255.;
var pointer_B = parseInt(pointer_splitted[3],16)/255.;
// Font properties
var graph_fontsize = document.myform.graph_fontsize.value;
var graph_fontname = document.myform.graph_fontname.
options[document.myform.graph_fontname.selectedIndex].text;
// Background color, using which we fill a rectangle before drawing
// the real image on it
var graph_background_splitted =
document.myform.graph_background.value.split(" ");
var graph_background_R = parseInt(graph_background_splitted[0],16)/255.;
var graph_background_G = parseInt(graph_background_splitted[1],16)/255.;
var graph_background_B = parseInt(graph_background_splitted[2],16)/255.;
// From here on document.myform is invalid ->
// weblink the graph (simple check of its existence):
document.write ("<HTML><body bgcolor=#ffffff><center><I><P>" +
graph_file.link(graph_file) + "</P>");
// then invoke the applet with parameters submitted by myform:
document.write ("<P><applet code=reader.class width=" + applet_width +
" height=" + applet_height + ">" +
"<param name=basex value=" + graph_width_pad + ">" +
"<param name=basey value=" + graph_height_pad + ">" +
"<param name=graph_file value=" + graph_file + ">" +
"<param name=graph_width value=" + graph_width + ">" +
"<param name=graph_height value=" + graph_height + ">" +
"<param name=pointer_size value=" + pointer_size + ">" +
"<param name=pointer_R value=" + pointer_R + ">" +
"<param name=pointer_G value=" + pointer_G + ">" +
"<param name=pointer_B value=" + pointer_B + ">" +
"<param name=graph_fontsize value=" + graph_fontsize + ">" +
"<param name=graph_fontname value=" + graph_fontname + ">" +
"<param name=graph_background_R value=" + graph_background_R + ">" +
"<param name=graph_background_G value=" + graph_background_G + ">" +
"<param name=graph_background_B value=" + graph_background_B + ">" +
"</applet></P>");
document.write ("<P><a href=reader.htm#open_Java_console>Open "+
"Java console</a> to view the saved data.</P></center></HTML>");
} // end init()
</SCRIPT>
And use this HTML:
<FORM name=myform>
<TABLE>
<TBODY>
<TR>
<TD>Graph filename</TD><!-- VALUE is ignored in IE4 and N4, and there is no way to -->
<TD><INPUT name=graph_file size=40 type=file></TD>
<!-- suggest file type. N4 inexplicably assumes HTML upload -->
<!-- GRAPH_FILE always local, and full pathname --></TR>
<TR>
<TD align=middle>or</TD><!-- GRAPH_FILENAME may be local, may be remote (through HTTP) -->
<TD><INPUT name=graph_filename size=40 value=example.gif> (type it in)</TD>
<!-- always interpreted as relative to this HTML document -->
<!-- GRAPH_FILE has higher priority than GRAPH_FILENAME field --></TR>
<P>
<TR>
<TD>Width height magn</TD>
<TD><INPUT name=graph_geom value="510 440 1.0"></TD></TR>
<P>
<TR>
<TD>Pointer size R G B</TD>
<TD><INPUT name=pointer value="2 36 64 8B"></TD></TR>
<P>
<TR>
<TD>Font size name</TD>
<TD><INPUT name=graph_fontsize size=3 value=12> <SELECT
name=graph_fontname> <OPTION>Default<OPTION
selected>TimesRoman<OPTION>Dialog<OPTION>SansSerif
<OPTION>Serif<OPTION>Monospaced
<OPTION>Helvetica<OPTION>Courier<OPTION>DialogInput
<OPTION>ZapfDingbats</OPTION></SELECT></TD></TR>
<P>
<TR>
<TD>Background R G B</TD>
<TD><INPUT name=graph_background value="FF FF FF"></TD></TR>
<P>
<TR>
<TD><INPUT onclick=init() type=submit value=Submit></TD>
<TD><INPUT type=reset value=Reset></TD></TR></P></TBODY></TABLE></FORM>
Back to the GraphReader applet page
New on the Java Boutique:
New Review:
Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling
API boasts simplicity, ease-of-integration, a well-rounded feature
set, and it's free!
New Applet:
Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA
sequences into three useful formats.
Elsewhere on internet.com:
WebDeveloper Java
Lots of Java information on webdeveloper.com
WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.
ScriptSearch Java
Hundreds of free Java code files to download.
jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.
|