DigitizeAny - A .NET Windows Digitization Tool


Background

I recently had been on the lookout for some application that can help me digitize plots/ graphs to obtain (X, Y) co-ordinates for use in other applications. I am sure engineers would be facing this situation day in and day out where they have to devise some means of getting large plots in their systems. There are some software available like Grapher and other very many off the internet. But the impeding factor apart from the cost was that the plots needed to be in image files (BMP,JPG or whatever) and those files had to be imported to digitize. Well, it sound reasonable, but if there are a large number of web pages/PDFs that contain the plot, then it can become pretty tedious. So I developed this small application in C# that can digitize points with (X,Y) off any parent window the plot might be in.

Outline

DigitizeAny uses Win32 API calls to obtain the coordinates of the cursor and then translates to the graph coordinates once set. Since a mouse click would essentially bring to focus the window where it is pointed to, this tool uses the key event to capture the coordinates of the cursor. So essentially, to use this tool, two key aspects need to be remembered:

  • Keep the DigitizeAny application in focus. Since this application would reside on top of all windows, you must make sure that you do not remove the focus from DigitizeAny (by clicking/tabbing or other means).
  • Use the keyboard button "X" to digitize or pickup the points from the plot (in the other window).The points picked up as client window coordinates would be shown in the title bar of the DigitizeAny application, and in the textbox as graph coordinates.
  • To set the graph coordinates, the application asks to enter the graph coordinate of two known point (one of them with a non-zero coordinate) and digitize those points on the client window (using X key on keyboard, as described above) to obtain the factor for recalculating the axes from the client screen coordinates.

Usage

1. Open the document to be digitized in the appropriate editor (e.g. Paint for images/Acrobat Reader for PDFs) and run the DigitizeAny application simultaneously.

2. Click on Assign Coordinates and select an initial coordinate (pref: (0, 0)).

3. Enter the translation coordinates in the space provided and click OK.

4. Select another coordinate, this time a non-zero coordinate for determination of translation factor.

5. Enter the translation coordinates in the space provided and click OK.

6. Click on Configure Axes to calculate translation of axes and make the application read in points.

7. Make sure the checkbox "Digitize Points" is checked to read in the values

8. The selected points appear in the textbox.

9.Any selected points can be copied to the clipboard by hitting "Copy" and pasted to your application.

Note :

The accuracy of the points digitized can be increased as per the application in the AddtoSet() by controlling accuracy of the calculated values Xfin and Yfin. Currently they are cast as integers.

Description

The code comprises of two main components:

Win32Class.cs: Class that contains the static functions from User32.dll (Win32) that needs to be invoked for obtaining the coordinates of the cursor position. User32.dll has functions that can transfer the screen coordinates to client window coordinates (ScreenToClient), Obtain the window handle (WindowFromPoint) that helps in achieving our objective.

The code fragment below indicates the Win32 functions and their declarations. A point type struct is also described to hold integer values.

DllImport("user32.dll", EntryPoint = "ScreenToClient")]
static public extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
[DllImport("user32.dll", EntryPoint = "ClientToScreen")]
static public extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
[DllImport("user32.dll", EntryPoint = "GetCursorPos")]
static public extern bool GetCursorPos(ref POINT lpPoint);
[DllImport("user32.dll", EntryPoint = "SetCursorPos")]
static public extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll", EntryPoint = "WindowFromPoint")]
static public extern IntPtr WindowFromPoint(POINT Point);
/// <summary>
/// POINT struct
/// </summary>
public struct POINT
{
public int X;
public int Y;
}

Form1.cs:  The form class which contains the C# code controlling the form behavior and calculating the axes transformation of the graph co-ordinates

The logic used in devising the transformation is obtaining the client window distance factor based on the two initial coordinates chosen and use that factor to transform the window coordinates to graph coordinates.

Some of the important features include:

KeyUp event, which is essential to pick up the digitized points. Since the key to reading the points from the other window and still capture the event in my application, I had to use some means to keep focus on my application. Else, I had to subscribe the events of the other windows and re-direct the call to my application for handling click events which would become circuitous. Hence, this keyup event was attached to the form and most of the controls in it, so that when the user would click X, the application would pickup the coordinates.

private void OnKeyEvent(object sender, System.Windows.Forms.KeyEventArgs kea) {
if(kea.KeyCode==Keys.X)
{
Win32Class.GetCursorPos(ref lpPointClick);
IntPtr ahWndClient = Win32Class.WindowFromPoint(lpPointClick);
//Get the Client coordinates
Win32Class.ScreenToClient(ahWndClient, ref lpPointClick);
this.Text =lpPointClick.X.ToString() + "," + lpPointClick.Y.ToString();
if(checkBox1.Checked==true)
{
if(checkBox2.Checked==true)
{
AddtoSet(textBox2.Text);//from the prompt
}
else
AddtoSet("");//add to the TextBox
}
}
}
/// <summary>
/// Displays the output for the points digitized after refactoring the client/graph
coordinate
/// </summary>
private void AddtoSet(String aValue)
{
Xfin=lpPointClick.X-Xadj;
Yfin=Yadj-lpPointClick.Y;
if(aValue=="")
textBox1.AppendText((int)(Xfin*Cx)+", "+(int)(Yfin*Cy)+ Environment.NewLine);
else
textBox1.AppendText((int)(Xfin*Cx)+", "+Int32.Parse(aValue)+ Environment. NewLine);
}

The GetCursorPos()obtains the cursor position in screen coordinates and to obtain the client coordinates is WindowFromPoint () invoked which returns the window handle. Once the window handle is obtained, the client coordinates can be obtained from ScreenToClient().

The function AddtoSet () displays the points digitized in the textbox.

/// <summary>
/// Formats the reference systems based on the points selected.
/// It finds the factor for X and Y for transforming from the client coordinates
/// to the graph coordinates
/// </summary>
private void SetReferenceSystem()
{
double numerator,denominator;
//Obtain the trans factor for the X coordinate
numerator=((Win32Class.POINT)oPointEnter[1]).X-((Win32Class.POINT)oPointEnter[0]).X;
denominator=((Win32Class.POINT)oPointClick[1]).X-((Win32Class.POINT)oPointClick[0]).X;
//Calculate Cx and Cy
Cx=numerator/denominator;
//Obtain the trans factor for Y coordinate
numerator=((Win32Class.POINT)oPointEnter[1]).Y-((Win32Class.POINT)oPointEnter[0]).Y; denominator=((Win32Class.POINT)oPointClick[1]).Y-((Win32Class.POINT)oPointClick[0]).Y;
Cy=(-1)* (numerator/denominator);
//Find the origin in terms of the client coordinates
//For X
if((((Win32Class.POINT)oPointEnter[0]).X)!=0)
Xadj=((Win32Class.POINT)oPointClick[0]).X-((Win32Class.POINT)oPointEnter[0]).X/Cx;
else
Xadj=((Win32Class.POINT)oPointClick[0]).X;
//For Y
if((((Win32Class.POINT)oPointEnter[0]).Y)!=0) Yadj=((Win32Class.POINT)oPointClick[0]).Y+((Win32Class.POINT)oPointEnter[0]).Y/Cy;
else
Yadj=((Win32Class.POINT)oPointClick[0]).Y;
}

SetReferenceSystem() contains the major operations that make the digitization possible. After obtaining the client coordinates of the cursor position (indicating the point to be digitized), it is just a matter of figuring out the ratio of the graph scale to the client window scale. Cx and Cy factors indicated in the code above obtain the factors described.

Xadj and Yadj obtain the distance of the point to be digitized from the origin of the graph coordinates.

Comments

There is also a facility in the application to enter the Y coordinate of the point, applicable for a contour plot. The interface is user friendly and pretty intuitive with messages prompts.

DigitizeAny is not certainly a sophisticated application that can meet all the needs of digitizing plots. And it certainly does not handle log curves/plots. But it does the basic job needed.

References 

MSDN Library.
Mr. Wayne Hartell, Developer, Haestad Methods Inc.
Mr. Diego Diaz, Developer, Haestad Methods Inc.