Sample LCAPI Application

The following sample application loads earthquake data from a CSV file, sends it in buffers of up to 100 events, and displays it as a time series in WorldWide Telescope.

Sample program🔗

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using System.Xml;

//
//  Quakes is a sample of the WorldWide Telescope LCAPI
//

namespace Quakes
{
    public partial class Quakes : Form
    {
        public Quakes()
        {
            InitializeComponent();
            //
            // Set defaults
            //
            listBox1.SelectedItem = "1000000";
            listBox2.SelectedItem = "Earth";
        }

        private void buttonExit_Click(object sender, EventArgs e)
        {
            Dispose(true);
        }

        private void buttonBrowse_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                richFilename.Text = openFileDialog1.FileName;
            }
        }

        //
        // Add messages to the list box
        //
        private void addMessage(string m)
        {
            int i = richMessages.Items.Add(m);
            richMessages.SelectedIndex = i;
        }

        //
        // Go!
        //
        private void buttonGo_Click(object sender, EventArgs e)
        {
            string line;
            string w;

            richMessages.Items.Clear();

            try
            {
                initWWTLayer();

                StreamReader sr = new StreamReader(richFilename.Text);

                //
                // Ignore first line of data (the headings)
                //
                w = sr.ReadLine();

                //
                // Read lines of data until there are none left
                //
                while ((w = sr.ReadLine()) != null)
                {
                    line = parseLine(w,false);
                    flushBufferToWWT(line);
                }

                sr.Close();

                //
                // Flush buffer for last time - empty string indicates this is the last flush
                //
                flushBufferToWWT("");

                //
                // Record the end
                //
                addMessage("Data transmission ended: " + totalEvents + " events");
            }
            catch (Exception ex)
            {
                addMessage(ex.Message);
            }

        }

        //
        // Change the event color
        //
        private void buttonColor_Click(object sender, EventArgs e)
        {
            if (colorDialog.ShowDialog() == DialogResult.OK)
            {
                buttonColor.BackColor = colorDialog.Color;
            }
        }

        int timeSlot;                   // Data column number for time
        int latSlot;                    // Data column number for latitude
        int lonSlot;                    // Data column number for longitude
        int depSlot;                    // Data column number for depth
        int magSlot;                    // Data column number for magnitude
        string layerId;                 // string GUID for the WWT Layer
        string lineBuffer;              // Buffer to hold events until they are sent
        int lineCountInBuffer;          // Number of events in the buffer
        int totalEvents;                // Count of total events transmitted
        DateTime lastTimeBufferFlushed; // Time of last transmission, used to check for time-out

        //
        // Locate the columns of the required data, they must be present but can be in any
        // order in the data file
        //

        private string initCSVData(string filename)
        {
            string w;           // line of data from data file

            timeSlot = -1;
            latSlot = -1;
            lonSlot = -1;
            depSlot = -1;
            magSlot = -1;

            //
            // First line (line 0) of the data file must contain the column headings
            //
            StreamReader sr = new StreamReader(filename);
            w = sr.ReadLine();
            string[] words = w.Split(new char[] { ',' });

            for (int i = 0; i < words.Length; i++)
            {
                if (words[i].IndexOf("Time", StringComparison.OrdinalIgnoreCase) != -1)
                    timeSlot = i;
                else if (words[i].IndexOf("Lat", StringComparison.OrdinalIgnoreCase) != -1)
                    latSlot = i;
                else if (words[i].IndexOf("Lon", StringComparison.OrdinalIgnoreCase) != -1)
                    lonSlot = i;
                else if (words[i].IndexOf("Depth", StringComparison.OrdinalIgnoreCase) != -1)
                    depSlot = i;
                else if (words[i].IndexOf("Mag", StringComparison.OrdinalIgnoreCase) != -1)
                    magSlot = i;
            }

            //
            // One of more heading is missing....
            //
            if (timeSlot == -1 || latSlot == -1 || lonSlot == -1 || depSlot == -1 || magSlot == -1)
                throw new Exception("Source file does not contain one or more of: time, lat, lon, depth, mag");

            //
            // Record a successful reading of the headings
            //
            addMessage(
              "Time = " + timeSlot.ToString() +
              " Lat = " + latSlot.ToString() +
              " Lon = " + lonSlot.ToString() +
              " Depth = " + depSlot.ToString() +
              " Mag = " + magSlot.ToString()
            );

            //
            // Extract the start time from the data file by reading line 1
            //

            w = sr.ReadLine();
            string startDate = parseLine(w, true);
            sr.Close();

            //
            // Record a successful reading of the start
            //
            addMessage("Start time = " + startDate);

            return startDate;
        }

        //
        // Convert a string time to the correct format
        //
        private string getUTCtime(string time)
        {
            DateTime d = DateTime.Parse(time);
            string s = d.ToUniversalTime().ToString();
            return s;
        }

        //
        // Parses a line from the data file
        //
        private string parseLine(string w, bool timeOnly)
        {
            string[] words = w.Split(new char[] { ',' });
            string line = "";

            string time = getUTCtime(words[timeSlot]);
            string lat = words[latSlot];
            string lon = words[lonSlot];
            string depth = words[depSlot];
            string mag = words[magSlot];

            if (timeOnly)
            {
                return time;
            }
            line = time + "\t" + lat + "\t" + lon + "\t" + depth + "\t" + mag + "\t";

            return line;
        }

        //
        // Create a new WWT layer
        //
        private void <a name="initWWTLayer">initWWTLayer()
        {
            //
            // Send a NEW command, extracting info from the data file and the UI
            //
            WebClient client = new WebClient();
            string startDate = initCSVData(richFilename.Text);
            string name = Path.GetFileNameWithoutExtension(richFilename.Text);
            string rate = listBox1.SelectedItem.ToString();
            string frame = listBox2.SelectedItem.ToString();
            string color = buttonColor.BackColor.ToArgb().ToString("X8"<font size="2">);
            string url = string.Format(
              "http://{0}:5050/layerApi.aspx?cmd=new&datetime={1}&timerate={2}&name={3}&frame={4}&color={5}",
              getIP().ToString(), startDate, rate, name, frame, color
            );
            // field string below is delimited by tabs, not spaces
            string response = client.UploadString(url, "TIME    LAT    LON    DEPTH    MAG");
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(response);
            XmlNode node = doc["LayerApi"];
            XmlNode child = node.ChildNodes[0];
            layerId = child.InnerText;

            //
            // Handle an error situation
            //
            if (layerId.Length != 36)
                throw new Exception("Invalid Layer Id received");

            //
            // Record a successful creation of a new layer
            //
            addMessage("New layer Id = " + layerId);

            //
            // Clear the buffer
            //
            lineBuffer = string.Empty;
            lineCountInBuffer = 0;
            totalEvents = 0;
            lastTimeBufferFlushed = DateTime.Now;
        }

        //
        // Utility to extract IP address
        //
        private IPAddress getIP()
        {
            IPAddress ipAddress = IPAddress.Loopback;

            // Find IPV4 Address

            foreach (IPAddress ipAdd in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
            {
                if (ipAdd.AddressFamily == AddressFamily.InterNetwork)
                {
                    if (CheckForWWTWebServer(ipAdd))
                    {
                        ipAddress = ipAdd;
                        break;
                    }
                }
            }
            return ipAddress;
        }

        private bool CheckForWWTWebServer(IPAddress address)
        {
            WebClient client = new WebClient();

            try
            {
                string version = client.DownloadString(
                  string.Format(
                    "http://{0}:5050/layerapi.aspx?cmd=version",
                    address.ToString()
                  )
                );
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(version);
                XmlNode node = doc["LayerApi"];

                if (node.OuterXml.Contains("Version"))
                {
                    // As version numbers are in the format xx.xx.xx.xx, an individual comparison of each
                    // number is necessary - a simple string comparison will give unreliable results

                    string[] versionNumbers = node.InnerText.Split('.');
                    if (versionNumbers[0].CompareTo("2") >= 0 &&
                        versionNumbers[1].CompareTo("8") >= 0)
                        return true;
                }
            }
            catch (Exception ex)
            {
                // handle exception
            }
            return false;
        }


        //
        // Buffer the data and send it to WWT every N events or every T time units
        //
        private void <a name="flushBufferToWWT">flushBufferToWWT(string line)
        {
            const int FlushThresholdInEventCount = 100;     // Flush the buffer when this number of events is stored
            const int FlushThresholdInSeconds = 2;          // Flush the buffer after this number of seconds has elapsed
            //
            // If line length is zero, this is the last transmission
            //
            if (line.Length > 0)
            {
                lineBuffer = lineBuffer + line + Environment.NewLine;
                lineCountInBuffer++;
            }
            DateTime now = DateTime.Now;

            //
            // Send the buffer if flushing for the last time OR the buffer is full OR the timeout has been reached
            //
            if ((line.Length == 0  lineCountInBuffer > 0) ||
                lineCountInBuffer == FlushThresholdInEventCount ||
                now.Subtract(lastTimeBufferFlushed) > TimeSpan.FromSeconds(FlushThresholdInSeconds))
            {
                //
                // Send an UPDATE command
                //
                WebClient client = new WebClient();

                string url = string.Format(
                  "http://{0}:5050/layerApi.aspx?cmd=update&id={1}",
                  getIP().ToString(), layerId
                );
                string response = client.UploadString(url, lineBuffer);
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(response);
                XmlNode node = doc["LayerApi"];
                string s = node.InnerText;

                //
                // Handle an error situation
                //
                if (s.Contains("Error"))
                {
                    throw new Exception(s);
                }
                //
                // Record a successful transmission
                //
                addMessage(now.ToString() + ": " + lineCountInBuffer.ToString() + " events sent");
                totalEvents += lineCountInBuffer;

                //
                // Clear the buffer
                //
                lineBuffer = string.Empty;
                lastTimeBufferFlushed = now;
                lineCountInBuffer = 0;
            }
        }
    }
}