WorldWide Telescope Layer Control API (LCAPI)

Note: This documentation is preliminary and subject to change.

This document describes how to write a software program to send data to WorldWide Telescope, using the Layer Control API (LCAPI). For a large amount of data the program might be a tool that reads data from a spreadsheet file, and then sends the appropriate fields of the data to WorldWide Telescope in appropriately sized buffers for visualization. For a smaller amount of data the entire file could be loaded in one go, or images or 3D models could be loaded. Time series data would typically be time-and-location dependent event data such as:

The time-series system does not lend itself to data that varies either its location or has a complex intensity - such as weather systems, or forest fires - though it is possible a limited approach to this sort of data may be useful. The system does lend itself to events that occur over an extended period of time, given the ability to greatly accelerate simulated time, and also events that decay very rapidly (lightning) or quite slowly (diseases) given the ability to control the decay time of the rendered graphic. Although most examples are of time series events on the Earth, the events can be on any of the supported Solar System bodies, or simply the Sky.

There are a number of constraints on the use of the LCAPI that should be understood before commencing with the design of the tool:

Table of Contents

See Also

For more information on WorldWide Telescope, refer to:


Design Overview

The Windows client version of WorldWide Telescope listens continuously, and without any setup process, for a certain format of communication on a particular port. The format of this communication is in the form of strings, that themselves are formatted in a specific way. An application may be quite short and only need a load call, or perhaps just need the new and update commands. Typically a simple application will go through the following steps, after WorldWide Telescope has been started:

  1. Establish the IP address of the computer running WorldWide Telescope. If this is the same computer as the application use the getIP utility, if not a utility will have to be written or acquired that establishes the remote IP address.
  2. Initialize and open a data file, or files, from a selected source.
  3. Initialize the connection with WorldWide Telescope with a new or load call, which will create a new layer within WorldWide Telescope, and return a layer Id number.
  4. If the new command is used:
    1. Fill a string buffer with data from the opened files.
    2. Transmit the data to WorldWide Telescope with an update call, using the layer Id number for reference.
    3. Repeat this step until all the data has been transmitted.
  5. Use the getprop, setprop, uisettings commands and parameters to control the view in WorldWide Telescope from the LCAPI application.
  6. Close down.

A more complex application may initialize several layers, or a layer group, within WorldWide Telescope, or may transmit the data under user control - enabling the user to have some choice over which data is rendered, the speed and time of the simulation, and other similar controls.

Development Environment

The samples and examples described in this document have been developed using Microsoft Visual Studio (2008 or 2010) and the .NET framework, and written in C#.

The suggested development approach is first to get the sample application working, then to modify it as required using the code examples for each command. Note that the code examples themselves are not run-able code, but just guidelines on how the calls can be made.

See Also


LCAPI Commands

The following commands can be used to control layers from the application. Note that command names are not case-sensitive, and that the general parameters can be included along with any other command.

Command
Description
activateHighlights the selected layer in the layer manager.
deleteSpecifies that a layer should be permanently deleted.
getprop

Used to retrieve a value of a single layer property. Remarks include the Table of Properties.

getpropsUsed to retrieve all the properties for a specified layer. Includes lists for Spreadsheet, 3d Model, Shapefile and ImageSet.
groupSpecifies that a layer group should be added.
layerlistReturns the structure of the layers and layer group names (in an XML document format) that are currently in the layer manager.
loadSpecifies a data file, and and some optional parameters, to apply to a new layer.
modeChanges the view to one of Earth, Planet, Sky, Panorama, SolarSystem.
moveChanges the view depending on the supplied parameter.
newSpecifies that a new layer should be created.
setpropUsed to specify a value of a single layer property. 
setpropsUsed to specify multiple properties for a layer.
stateRequests some details of the current view.
uisettingsUsed to change user interface settings, without altering the layer data.
updateSpecifies that the data attached to this command should be added to the layer.
versionReturns the version number of the running version of the LCAPI.
general parametersParameters that can be applied to any of the commands.

See Also


activate

The activate layer command will highlight the selected layer in the layer manager.

Remarks

Required Parameter
Description
&idSpecifies the id number of the layer.

Return Value

The following string will be included in the response if the call is successful:

<Status>Success</Status>

If the call is not successful the following string may be included in the response, or other errors may be returned:

<Status>Error - Invalid layer ID</Status> 

See Also


delete

The delete command specifies that a layer should be permanently deleted.

Remarks

All sub-components of the layer will also be deleted.
Required Parameter
Description
&idSpecifies the id number of the layer.

Return Value

The following string will be included in the response if the call is successful:

<Status>Success</Status>

If the call is not successful the following string may be included in the response, or other errors may be returned:

<Status>Error - Invalid layer ID</Status>

Example Code

    //
    // Send a DELETE command
    //
    WebClient client = new WebClient();
    string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=delete&id={1}", getIP().ToString(), layerId);
    string response = client.UploadString(url, "");
    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);
    }

See Also


getprop

The getprop command is used to retrieve a value of a single layer property.

Remarks

Required Parameters
Description
&idSpecifies the id number of the layer.
&propnameProperty name, one from the table below.

Table of Properties

The following table lists the properties that can be get or set on a layer (note that the property names and Enum values are case-sensitive). Also refer to the Notes below the table.

Property
Type
Description
Layer Type
Default
AltColumnIntColumn number containing the altitude/depth data. Spreadsheet-1
AltTypeEnumOne of: Depth, Altitude, SeaLevel, Terrain.Spreadsheet SeaLevel
AltUnitEnumOne of: Meters, Feet, Inches, Miles, Kilometers, AstronomicalUnits, LightYears, Parsecs, MegaParsecs.SpreadsheetMeters for altitude and kilometers for depth.
AstronomicalBooleanTrue if the data is astronomical rather than terrestrial. If this is set to true the declination will be by default the Latitude column, and the right ascension the Longitude column.AllFalse
BeginRangeDateTimeDate and time of the first data entry in a time series,
in one the formats (month/day/year):
"1/1/2010 11:00:00 PM"
"1/1/2010 11:30 AM"
"1/1/2010 11 am"
"1/1/2000"
"1/2000"
Spreadsheet Maximum system value
CartesianCustomScale DoubleUsed to divide the Cartesian co-ordinate values to a custom scale. If this value was set to "0.01" and the scale to Meters, the custom scale would be "centimeters".Spreadsheet 1
CartesianScaleEnumIf the CoordinatesType is not Spherical then the co-ordinate system uses X,Y and Z values.
One of: Meters, Feet, Inches, Miles, Kilometers, AstronomicalUnits, LightYears, Parsecs, MegaParsecs, Custom.
SpreadsheetMeters
ColorMapEnumOne of: Same_For_All, Group_by_Values.Spreadsheet Same_For_All
ColorMapColumnIntColumn number containing the color map data. Columns are numbered from zero, -1 indicates there is no data for this.Spreadsheet-1
ColorValueStringString containing ARGB value of the color, in the format:
"ARGBColor:255:255:255:255".
All White
CoordinatesTypeEnumOne of: Spherical, Rectangular, Orbital.

Spherical applies when the reference frame is a sphere, and the coordinates will be Lat/Lng or RA/Dec.

Rectangular applies if there are X,Y and Z coordinates. 0,0,0 in this case is the center of the reference frame.
Spreadsheet Spherical
DecayFloatDecay rate of the visualization, in days, in the range 0.00025 to 4096. Spreadsheet 16
EnabledBooleanTrue if the layer is enabled.AllTrue
EndDateColumnIntColumn containing the end date/time data.Spreadsheet-1
EndRangeDateTimeDate and time of the last data entry in a time series,
in one the formats (month/day/year):
"1/1/2010 11:00:00 PM"
"1/1/2010 11:30 AM"
"1/1/2010 11 am"
"1/1/2000"
"1/2000"
Spreadsheet Minimum system value
EndTimeDateTimeDate and time to end the visualizations, and return to the start time in the case of an auto loop.  This property is visible in the Lifetime dialog for a layer.
Format (month/day/year):
"1/1/2010 11:00:00 PM"
"1/1/2010 11:30 AM"
"1/1/2010 11 am"
"1/1/2000"
"1/2000"
All Maximum system time.
GeometryColumnIntColumn containing the geometry data.Spreadsheet-1
HeadingDoubleHeading angle  of the model in radians.3D Model0
HyperlinkColumnIntColumn containing the hyperlink data.Spreadsheet-1
HyperlinkFormatString  Spreadsheet ""
FadeSpanTimeThe time it takes to fade out the graphic.
This property is visible in the Lifetime dialog for the layer and is shown in days in the dialog. When using the SDK use the format D.HH:MM:SS, so for one day use 1.00:00:00.
All0
FadeTypeEnumOne of: In, Out, Both, None.AllNone
FlipVBooleanTrue if the textures for the 3D model should be flipped.3D ModelFalse
LatColumnIntColumn number for the Latitude data.Spreadsheet-1
LngColumnIntColumn number for the Longitude data.Spreadsheet-1
MarkerColumnIntColumn number for the marker data.Spreadsheet-1
MarkerIndexIntIndex number of marker graphic selected, or -1 if no marker has been selected.Spreadsheet -1
MarkerMixEnumOne of: Same_For_All, Group_by_Values.Spreadsheet Same_For_All
MarkerScaleEnumOne of: Screen, World.
If this is set to Screen the graphic will not change in size as the viewer zooms in and out. If it is set to World the graphic (Pushpin or Gaussian) will scale with the view.
SpreadsheetWorld
NameStringName of the layer. All "New layer"
NameColumnIntColumn containing the event name data.Spreadsheet-1
OpacityFloatOpacity of the layer graphics from 0.0 to 1.0.All1
PitchDoublePitch angle of the model in radians. 3d Model 0
PlotTypeEnumOne of: Gaussian, Point, PushPin.SpreadsheetGaussian
PointScaleTypeEnumOne of: Linear, Power, Log, Constant, StellarMagnitude.Spreadsheet Power
RaUnitsEnumWhen the longitude column is being used for RA in astronomical data.
One of: Hours, Degrees.
SpreadsheetHours
RollDouble Roll angle of the model in radians.3D Model0
ScaleVector3dScale of the model in x, y and z dimensions.
Format: 1,1,1
3D Model 1,1,1
ScaleFactorFloatScale factor to apply to the magnitude of the event.Spreadsheet1
ShowFarSideBooleanTrue if the data points on the invisible side of the body should be shown. Spreadsheet False
SizeColumnIntColumn containing the size (magnitude) data.Spreadsheet-1
SmoothBooleanTrue if the 3D Model should have its normals smoothed.3D ModelTrue
StartDateColumnIntColumn containing the start date/time data.Spreadsheet-1
StartTimeDateTimeDate and time to start the visualizations. This property is visible in the Lifetime dialog for the layer. Formats (month/day/year):
"1/1/2010 11:00:00 PM"
"1/1/2010 11:30 AM"
"1/1/2010 11 am"
"1/1/2000"
"1/2000"
All Minimum system time
TimeSeriesBooleanTrue if the layer should be treated as time series data.SpreadsheetFalse
TranslateVector3dTranslation (movement offset) of the model in x, y and z dimensions, in units of the model size.
Format: 1,1,1
3D Model0,0,0
XAxisColumnIntColumn number for X data.Spreadsheet-1
YAxisColumnIntColumn number for Y data.Spreadsheet-1
ZAxisColumnIntColumn number for Z data.Spreadsheet-1
XAxisReverseBooleanTrue if the direction of the X axis is reversed. Spreadsheet False
YAxisReverseBooleanTrue if the direction of the Y axis is reversed. Spreadsheet False
ZAxisReverseBooleanTrue if the direction of the Z axis is reversed. Spreadsheet False

Notes

Return Value

If the call is successful the response will contain the following string:

<Status>Success</Status><Layer propertyName="propertyValue"</Layer></LayerApi>

Where propertyName is the property name requested, and propertyValue is the value returned. For example:

Get properties
<LayerApi><Status>Success</Status><Layer AltType="Depth"</Layer></LayerApi>
<LayerApi><Status>Success</Status><Layer AltColumn="3"</Layer></LayerApi>
<LayerApi><Status>Success</Status><Layer Astronomical="False"</Layer></LayerApi>
<LayerApi><Status>Success</Status><Layer BeginRange="1/9/2009 11:44:38 AM"</Layer></LayerApi>
<LayerApi><Status>Success</Status><Layer EndRange="9/9/2009 11:13:52 PM"</Layer></LayerApi>
<LayerApi><Status>Success</Status><Layer Decay="16"</Layer></LayerApi>
<LayerApi><Status>Success</Status><Layer Name="EarthQuakes2009"</Layer></LayerApi>
<LayerApi><Status>Success</Status><Layer FadeSpan="00:00:00"</Layer></LayerApi>
<LayerApi><Status>Success</Status><Layer MarkerIndex="-1"</Layer></LayerApi>

If the call is not successful the following string may be included in the response, or other errors may be returned:

<Status>Error - Invalid parameter</Status>

Example Code

Get altitude units
WebClient client = new WebClient();
string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=getprop&id={1}&propname={2}", getIP().ToString(), layerId, "AltUnit");
string response = client.UploadString(url, "");
response 
<?xml version="1.0" encoding="utf-8"?><LayerApi><Status>Success</Status><Layer AltUnit="Kilometers"</Layer></LayerApi>

See Also


getprops

The getprops command is used to retrieve all the properties for a specified layer.

Remarks

Refer to the table of properties listed for the getprop command.

Required Parameters
Description
&idSpecifies the id number of the layer.

Return Value

If the call is successful the response string will contain the following string:

<Status>Success</Status>

If the call is not successful the following string may be included in the response, or other errors may be returned:

<Status>Error - Invalid layer ID</Status> 

Example Code

The following example gets all the properties from any of the different types of layer. Note that the response string does not include newlines, these have been added for readability.

get all properties
 private void buttonGetProperties()
 {
    try
    {
        WebClient client = new WebClient();
        string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=getprops&id={1}", getIP().ToString(), layerId);
        string response = client.UploadString(url, "");
        XmlDocument doc = new XmlDocument();

        doc.LoadXml(response);
        XmlNode node = doc["LayerApi"];
        if (node.InnerText.Contains("Success"))
        {
            XmlNode node2 = node["Layer"];
            if (node2 != null)
            {
                string propName;
                string propValue;
                
                for (int i = 0; i < node2.Attributes.Count; i++)
                {
                    propName = node2.Attributes[i].Name;
                    propValue = node2.Attributes[i].Value;
                    // Do something with the name/value pair
                }
            }
            else
            {
                // No properties returned
            }
        } 
        else
        {
           throw new Exception(response);
        }
    }
    catch (Exception ex)
    {
        // Handle exception
    }
}
Spreadsheet
<?xml version='1.0' encoding='UTF-8'?>
<LayerApi>
<Status>Success</Status>
<Layer 
   Class=\"SpreadSheetLayer\" 
   TimeSeries=\"True\" 
   BeginRange=\"1/9/2009 11:44:38 AM\" 
   EndRange=\"9/9/2009 11:13:52 PM\" 
   Decay=\"16\" 
   CoordinatesType=\"Spherical\" 
   LatColumn=\"1\" 
   LngColumn=\"2\" 
   GeometryColumn=\"-1\" 
   XAxisColumn=\"-1\" 
   YAxisColumn=\"-1\" 
   ZAxisColumn=\"-1\" 
   XAxisReverse=\"False\" 
   YAxisReverse=\"False\" 
   ZAxisReverse=\"False\" 
   AltType=\"Depth\" 
   RaUnits=\"Hours\" 
   MarkerMix=\"Same_For_All\" 
   MarkerColumn=\"-1\" 
   ColorMapColumn=\"-1\" 
   PlotType=\"Gaussian\" 
   MarkerIndex=\"-1\" 
   ShowFarSide=\"False\" 
   MarkerScale=\"World\" 
   AltUnit=\"Kilometers\" 
   CartesianScale=\"Meters\" 
   CartesianCustomScale=\"1\" 
   AltColumn=\"3\" 
   StartDateColumn=\"0\" 
   EndDateColumn=\"-1\" 
   SizeColumn=\"4\" 
   NameColumn=\"0\" 
   HyperlinkFormat=\"\" 
   HyperlinkColumn=\"-1\" 
   ScaleFactor=\"1\" 
   PointScaleType=\"Power\" 
   Opacity=\"1\" 
   StartTime=\"1/1/0001 12:00:00 AM\" 
   EndTime=\"12/31/9999 11:59:59 PM\" 
   FadeSpan=\"00:00:00\" 
   FadeType=\"None\" 
   Name=\"EarthQuakes2009\" 
   ColorValue=\"ARGBColor:255:255:0:0\" 
   Enabled=\"True\" 
   Astronomical=\"False\" />
</LayerApi>
3D Model
<?xml version='1.0' encoding='UTF-8'?>
<LayerApi>  
<Status>Success</Status>  
<Layer 
   Class=\"Object3dLayer\" 
   FlipV=\"False\" 
   Smooth=\"True\" 
   Heading=\"0\" 
   Pitch=\"0\" 
   Roll=\"0\" 
   Scale=\"1, 1, 1\" 
   Translate=\"0, 0, 0\" 
   Opacity=\"1\" 
   StartTime=\"1/1/0001 12:00:00 AM\" 
   EndTime=\"12/31/9999 11:59:59 PM\" 
   FadeSpan=\"00:00:00\" 
   FadeType=\"None\" 
   Name=\"aurora\" 
   ColorValue=\"ARGBColor:255:255:0:0\" 
   Enabled=\"True\" 
   Astronomical=\"False\" />
</LayerApi>
Shapefile
<?xml version='1.0' encoding='UTF-8'?>
<LayerApi>  
<Status>Success</Status>  
<Layer 
   Class=\"ShapeFileRenderer\" 
   Opacity=\"1\" 
   StartTime=\"1/1/0001 12:00:00 AM\" 
   EndTime=\"12/31/9999 11:59:59 PM\" 
   FadeSpan=\"00:00:00\" 
   FadeType=\"None\" 
   Name=\"state_bounds\" 
   ColorValue=\"ARGBColor:255:255:0:0\" 
   Enabled=\"True\" 
   Astronomical=\"False\" />
</LayerApi>
ImageSet
<?xml version='1.0' encoding='UTF-8'?>
<LayerApi>  
<Status>Success</Status>  
<Layer 
   Class=\"ImageSetLayer\" 
   Opacity=\"1\" 
   StartTime=\"1/1/0001 12:00:00 AM\" 
   EndTime=\"12/31/9999 11:59:59 PM\" 
   FadeSpan=\"00:00:00\" 
   FadeType=\"None\" 
   Name=\"Bathymetry\" 
   ColorValue=\"ARGBColor:255:255:0:0\" 
   Enabled=\"True\" 
   Astronomical=\"False\" />
</LayerApi>

See Also


group

The group command specifies that a layer group should be added.

Remarks

Layer groups are just an organizational aid when using the layer manager. The user will be able to collapse and expand groups in the Layer Manager, and have groups that are sub-sets of other groups.

Required Parameter
Description
&nameA unique name for the layer group.
&frameThe reference frame of the group. This can be a layer group created with the group command, or one of:
Earth, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Io, Ganymede, Callisto, Europa, Sun, ISS.

Return Value

The following string will be included in the response if the call is successful:

<Status>Success</Status>

If the call is not successful the following string may be included in the response, or other errors may be returned:

<Status>Error - Invalid layer ID</Status>

Example Code

  private void createLayerGroup(string frame, string name)
  {
      try
      {
          WebClient client = new WebClient();
          string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=group&frame={1}&name={2}", getIP().ToString(), frame, name);
          string response = client.UploadString(url, "");
          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);
          }
      }
      catch (Exception ex)
      {
             
      }
  }

See Also


layerlist

The layerlist command returns the structure of the layers and layer group names (in an XML document format) that are currently in the layer manager.

Remarks

Optional Parameter
Description
Default
&layersonlyTrue indicates that only layers, and not reference frames or group names, should be returned.False

Return value

If the call is successful a string will be returned that is an XML document. Refer to the Example Code for the format and contents of the document.

If the call is not successful the following string may be included in the response, or other errors may be returned:

<Status>Error - Invalid layer ID</Status>

Example Code

The following code will result in the response given below, though note that one layer (EarthQuakes2009) and one group (New Mercury Group) have been added to the layer manager prior to the layerlist call.

layerlist
//
// Recursive scanning of child nodes
//
private void scanNode(XmlNode node)
{
   int i,n;
   //
   // First loop through the attributes of the node
   //
 
   for (i = 0; i < node.Attributes.Count; i++)
   {
      // Do something with the node.Attributes[i].Name and node.Attributes[i].Value
   }      

   //
   // Next recurse the node tree
   //
   for (n = 0; n < node.ChildNodes.Count; n++)
   {
      scanNode(node.ChildNodes[n])
   }
}
//
// Get the current list of Reference Frames, groups and layers
//
private void getLayerlist(string layersonly)
{
   try
   {
     WebClient client = new WebClient();
     string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=layerlist&layersonly={1}", getIP().ToString(), layersonly);
     string response = client.UploadString(url, "");
     XmlDocument doc = new XmlDocument();


      doc.LoadXml(response);
      XmlNode node = doc["LayerApi"];
      if (node.InnerText.Contains("Success"))
      {

         XmlNode node2 = node["LayerList"];

         if (node2 != null)
         {
            // Top level items returned = node2.ChildNodes.Count
            scanNode(node2);
         }
         else
         {
            // No items returned
         }
      }
      else
      {
         throw new Exception(response);
      }
   }
   catch (Exception ex)
   {
      // Handle exception
   }
}
response  (layersonly = "false")
<?xml version='1.0' encoding='UTF-8'?>
<LayerApi>  
<Status>Success</Status>  
<LayerList>    
<ReferenceFrame Name=\"Sun\" Enabled=\"True\">      
<ReferenceFrame Name=\"Mercury\" Enabled=\"True\">        
    <LayerGroup Name=\"New Mercury Group\" Enabled=\"True\" />      
</ReferenceFrame>      
<ReferenceFrame Name=\"Venus\" Enabled=\"True\" />      
<ReferenceFrame Name=\"Earth\" Enabled=\"True\">        
    <Layer Name=\"EarthQuakes2009\" ID=\"712cdef2-907f-4fc5-ae2c-c57488be4517\" Type="SpreadSheetLayer" Enabled=\"True\" />        
    <ReferenceFrame Name=\"Moon\" Enabled=\"True\" />        
    <ReferenceFrame Name=\"ISS\" Enabled=\"True\" />      
</ReferenceFrame>      
<ReferenceFrame Name=\"Mars\" Enabled=\"True\" />      
<ReferenceFrame Name=\"Jupiter\" Enabled=\"True\">        
    <ReferenceFrame Name=\"Io\" Enabled=\"True\" />        
    <ReferenceFrame Name=\"Europa\" Enabled=\"True\" />        
    <ReferenceFrame Name=\"Ganymede\" Enabled=\"True\" />
        <ReferenceFrame Name=\"Callisto\" Enabled=\"True\" />      
</ReferenceFrame>      
<ReferenceFrame Name=\"Saturn\" Enabled=\"True\" />      
<ReferenceFrame Name=\"Uranus\" Enabled=\"True\" />      
<ReferenceFrame Name=\"Neptune\" Enabled=\"True\" />
<ReferenceFrame Name=\"Pluto\" Enabled=\"True\" />    
</ReferenceFrame>  
</LayerList>
</LayerApi>
response (layersonly = "true")
<?xml version='1.0' encoding='UTF-8'?>
<LayerApi>
  <Status>Success</Status>
  <LayerList>
    <Layer Name=\"EarthQuakes2009\" ID=\"712cdef2-907f-4fc5-ae2c-c57488be4517\" Type="SpreadSheetLayer"  Enabled=\"True\" />
  </LayerList>
</LayerApi>

See Also


load

The load command specifies a data file, and and some optional parameters, to apply to a new layer.

Remarks

Files that can be loaded using this commend include spreadsheet data (comma or tab delimited), shape files (.shp), 3D model files (.3ds), and WTML files containing ImageSet references. In the latter case the first image set found in the file will be loaded. Refer to the WorldWide Telescope Data Files Reference for more details on WTML files.

If a spreadsheet of data is loaded, the load command will initiate an import wizard of WorldWide Telescope, in order to select the appropriate columns, timing data, and so on.  An import wizard is not automatically invoked by the other data file types.

Note that parameter names are case-sensitive.

Required Parameter
Description
 
&frameThe reference frame of the group. This can be an existing layer group name, or one of:
Earth, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Io, Ganymede, Callisto, Europa, Sun, ISS.
 
&filenameThe full path of the file to load. 
    
Optional Parameter
 
Default
&nameA friendly name for the layer. "New Layer"
&colorARBG hex value of the color to be used when rendering the events of the layer.FFFFFFFF
(white)
&startdateWith time series data, the date and time to start the visualization for this layer. This could for example be slightly earlier than the date of the first event in the actual data. For example: "1/1/2000 12:30:30 AM".
Formats (month/day/year):
"1/1/2010 11:00:00 PM"
"1/1/2010 11:30 AM"
"1/1/2010 11 am"
"1/1/2000"
"1/2000"
The System minimum date value
&enddateWith time series data, the date and time to end the visualization for this layer.The System maximum date value
&fadetypeFades the data visualization. One of: In, Out, Both or None.None
&faderangeFade time in days.Zero

Return Value

The following string will be included in the response string:

<LayerApi><NewLayerID>nnnn</NewLayerID></LayerApi>

Where nnnn is the layer id, which will be a GUID in string format.

If the call is not successful the following string may be included in the response, or other errors may be returned:

<Status>Error - Could not Load Layer</Status>

Example Code

Note that the example code uses datetime, one of the general parameters, to initiate the visualization of the data immediately.

private void loadDatafile(string name, string frame, string filename, string datetime)
{
    try
    {
        WebClient client = new WebClient();
        string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=load&frame={1}&filename={2}&name={3}&datetime={4}",
getIP().ToString(), frame, filename, name, datetime);
        string response = client.UploadString(url, "");
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(response);
        XmlNode node = doc["LayerApi"];
        XmlNode child = node.ChildNodes[0];
        layerId = child.InnerText;

        string s = node.InnerText;

        //
        // Handle an error situation
        //
        if (s.Contains("Error"))
        {
            throw new Exception(s);
        }
    }
    catch (Exception ex)
    {
            
    }
}

See Also


mode

The mode command changes the view to one of Earth, Planet, Sky, Panorama, SolarSystem.

Remarks

This command does not take any parameters.

Return value

The following string will be included in the response if the call is successful:

<Status>Success</Status>

Example code

Change mode
WebClient client = new WebClient();
string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=mode&lookat={1}", getIP().ToString(), "SolarSystem");
string response = client.UploadString(url, "");

See Also


move

The move command changes the view depending on the supplied parameter.

Remarks

One move parameter must be supplied, &move=nnnn, where nnnn is one of the following:

Required Parameter
Description
ZoomInZoom in on the current view.
ZoomOutZoom out of the current view.
UpMove the current view up.
DownMove the current view down.
LeftMove the current view left.
RightMove the current view right.
ClockwiseRotate the view clockwise 0.2 of one radian.
CounterClockwiseRotate the view counterclockwise 0.2 of one radian.
TiltUpAngle the view up 0.2 of one radian.
TiltDownAngle the view down 0.2 of one radian.
FinderCurrently unimplemented.

Return value

The following string will be included in the response if the call is successful:

<Status>Success</Status>

The following code will be included in the response if the move parameter is invalid:

<Status>Error - Invalid parameter</Status>

Example code

WebClient client = new WebClient();
string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=move&move={1}", getIP().ToString(), "ZoomIn");
string response = client.UploadString(url, "");

See Also


new

The new command specifies that a new layer should be created.

Remarks

The new command will request that an entirely new layer be created, with the following parameters (note that the parameter names are case-sensitive):

Required
Parameter
Description
 
&frameThe reference frame of the layer. This can be one of:
Earth, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Io, Ganymede, Callisto, Europa, Sun, ISS.
 
    
Optional Parameters
 
Default
&nameA friendly name for the layer. "New Layer"
&colorARBG hex value of the color to be used when rendering the events of the layer.FFFFFFFF
(white)
&startdateWith time series data, the date and time to start the visualization for this layer. This could for example be slightly earlier than the date of the first event in the actual data. For example: "1/1/2000 12:30:30 AM".
Formats (month/day/year):
"1/1/2010 11:00:00 PM"
"1/1/2010 11:30 AM"
"1/1/2010 11 am"
"1/1/2000"
"1/2000"
The System minimum date value
&enddateWith time series data, the date and time to end the visualization for this layer.The System maximum date value
&fadetypeFades the data visualization. One of: In, Out, Both or None. None
&faderangeFade time in days.Zero

The new command is embedded in a formatted string (see the Example Code). This formatted string forms the first of two parameters to the WebClient UploadString method.

The second string parameter to UploadString should contain a comma or tab delimited string containing the heading names of the columns in the layer. The supported column names are listed in the following table. The names of the columns in the data file must contain the names listed here (so "Latitude" is acceptable for "Lat", or "dMag" acceptable for "Mag", for example).

Column Heading
Description
TimeUTC time, for example: "1/1/2000 12:02:46 AM"
LatLatitude in decimal degrees.
LonLongitude in decimal degrees.
RARight ascension in decimal degrees.
DecDeclination in decimal degrees.
DepthDepth in kilometers.
AltitudeAltitude in meters.
MagMagnitude as a floating point number.
XX co-ordinate, if a Rectangular co-ordinate system is being used.
YY co-ordinate.
ZZ co-ordinate.

Return Value

The following string will be included in the response string:

<LayerApi><NewLayerID>nnnn</NewLayerID></LayerApi>

Where nnnn is the layer id number, which will be a GUID in string format. If a GUID is not received, then the call was not successful.

Example Code

Note that the example code uses datetime, one of the general parameters, to initiate the visualization of the data immediately.

   //
   // Send a NEW command, extracting info from the data file and the UI
   //
   WebClient client = new WebClient();
   string datetime =  "1/1/2009 12:00:00 AM";
   string name = "Earthquakes 2009";
   string rate = "10000";
   string frame = "Earth";
   string color = "FFFF0000";

   string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=new&datetime={1}&timerate={2}&name={3}&frame={4}&color={5}",  getIP().ToString(), datetime, 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");

   //

For an example of the use of this command in the sample application, see initWWTLayer.

See Also


setprop

The setprop command is used to specify a value for a single layer property.

Remarks

Required Parameters
Description
&idSpecifies the id number of the layer.
&propnameProperty name. Refer to the table of properties listed for the getprop command.
&propvalueProperty value in string form.

Return Value

If the call is successful, then the response will contain the string:

<Status>Success</Status>

If the call is not successful the following string may be included in the response, or other errors may be returned:

<Status>Error - Invalid parameter</Status> 

Example Code

Set Opacity property
 WebClient client = new WebClient();
 string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=setprop&id={1}&propname={2}&propvalue={3}", getIP().ToString(), layerId,"Opacity", "0.5");
 string response = client.UploadString(url, "");

See Also


setprops

The setprops command is used to specify multiple properties for a layer.

Remarks

Required Parameters
Description
&idSpecifies the id number of the layer.

Note that the second string parameter to the UploadString method contains the xml encoding of the properties. The string should be a correctly formatted XML file, with xml, LayerApi and Layer entries. Each attribute to the Layer entry should be a property name/property value pair.

This method can be used to set a single property, though the setprop command is specifically designed for this.

Refer to the table of properties listed for the getprop command.

Return Value

If the call is successful, then the response will contain the string:

<Status>Success</Status>

If the call is not successful the following string may be included in the response, or other errors may be returned:

<Status>Error - Invalid parameter</Status>

Example Code

Set BeginRange and Decay properties
string propertyXML = "<?xml version='1.0' encoding='UTF-8'?><LayerApi><Layer BeginRange=\"1/9/2008 11:44:38 AM\" Decay=\"5.5\" /> </LayerApi>"

WebClient client = new WebClient();
string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=setprops&id={1}", getIP().ToString(), layerId);
string response = client.UploadString(url, propertyXML);

See Also


state

The state command requests some details of the current view.

Remarks

This command does not take any parameters. The details returned vary slightly depending on the view mode, and include the current view mode, latitude (or declination) in decimal degrees, longitude (or right ascension) in decimal degrees, zoom factor (0 to 360), angle in radians, rotation in radians, current UTC time, time rate, reference frame, a view token, and zoom text.

Return Value

If the call is successful the following string will be returned:

<LayerApi><Status>Success</Status><ViewState> state information </ViewState></LayerApi>

Example code

Get state
private void getState()
{
   try
   {
       WebClient client = new WebClient();
       string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=state", getIP().ToString());
       string response = client.UploadString(url, "");
       XmlDocument doc = new XmlDocument();
       doc.LoadXml(response);
       XmlNode node = doc["LayerApi"];
       if (node.InnerText.Contains("Success"))
       {
           XmlNode node2 = node["ViewState"];
              
           if (node2 != null)
           {
               string propName;
               string propValue;
               
               for (int i = 0; i < node2.Attributes.Count; i++)
               {
                   propName = node2.Attributes[i].Name;
                   propValue =  node2.Attributes[i].Value;
                   // Do something with the name value pair
               }
           }
           else
           {
              // Success message was a false positive, no state was returned
           }
       }
       else
       {
           throw new Exception(response);
       }
   }
   catch (Exception ex)
   {
      // Handle the exception
   }
}
State information for each of the five modes:
<ViewState 
   lookat=\"Earth\" 
   lat=\"86.1800140779305\" 
   lng=\"4.56191579793961\" 
   zoom=\"30.9237645312\" 
   angle=\"-1.52\" 
   rotation=\"0\" 
   time=\"2/23/2011 7:03:31 PM\" 
   timerate=\"1\" 
   ReferenceFrame=\"Earth\" 
   ViewToken=\"SD8834DFA\" 
   zoomText=\"5085 km\">
</ViewState>


<ViewState 
   lookat=\"Planet\" 
   lat=\"86.1800140779305\" 
   lng=\"4.58610934632668\" 
   zoom=\"360\" 
   angle=\"-1.52\" 
   rotation=\"0\" 
   time=\"2/23/2011 7:04:19 PM\" 
   timerate=\"1\" 
   ReferenceFrame=\"Mars\" 
   ViewToken=\"SD8834DFA\" 
   zoomText=\"59200 km\">
</ViewState>


<ViewState 
   lookat=\"Sky\" 
   ra=\"10.2626615902966\" 
   dec=\"46.1799337621283\" 
   zoom=\"30.9237645312\" 
   rotation=\"0\" 
   time=\"2/23/2011 7:04:51 PM\" 
   timerate=\"1\" 
   ReferenceFrame=\"Space\" 
   ViewToken=\"SD8834DFA\" 
   zoomText=\"05:09:14\">
</ViewState>


<ViewState 
   lookat=\"Panorama\" 
   lat=\"-3.07957843596986\" 
   lng=\"-39.9196238171819\" 
   zoom=\"117.9648\" 
   angle=\"-1.52\" 
   rotation=\"0\" 
   time=\"2/23/2011 7:05:35 PM\" 
   timerate=\"1\" 
   ReferenceFrame=\"Panorama\" 
   ViewToken=\"SD8834DFA\" 
   zoomText=\"19:39:39\">
</ViewState>


<ViewState 
   lookat=\"SolarSystem\" 
   lat=\"2.25294244185011\" 
   lng=\"262.382989924732\" 
   zoom=\"0.000578857064151695\" 
   angle=\"-1.52\" 
   rotation=\"0\" 
   time=\"2/23/2011 7:06:13 PM\" 
   timerate=\"1\" 
   ReferenceFrame=\"Sun\" 
   ViewToken=\"SD8834DFA\" 
   zoomText=\"38636 km\">
</ViewState>

See Also


uisettings

The uisettings command is used to change user interface settings, without altering the layer data. Note that the spelling errors in the names of the properties must be matched.

Setting Name
Setting Type
AutoHideContextTrue or False
AutoHideTabsTrue or False
AutoRepeatTourTrue or False
AutoRepeatTourAllTrue or False
ConstellationBoundryColorRGB color (default, "MidnightBlue")
ConstellationFigureColorRGB color (default, "DarkRed")
ConstellationFiguresFileString (a filename)
ConstellationSelectionColorRGB color (default, "DarkGoldenrod")
ContextSearchFilterString (default "Unfiltered")
DomeTiltDouble (default "0")
DomeTypeIndexInteger (default, "0")
DomeViewTrue or False
EclipticColorRGB color (default, "CornflowerBlue ")
FollowMouseOnZoomTrue or False
FovCameraInteger (default, "0")
FovColorRGB color (default, "white")
FovEyepieceInteger (default, "0")
FovTelescopeInteger (default, "0")
FullScreenToursTrue or False
GridColorRGB color (default, "64, 64, 64")
ImageQualityInteger: 0 to 100 (default, 100)
LargeDomeTexturesTrue or False
LastLookAtModeInteger (default, "2")
LineSmoothingTrue or False
ListenModeTrue or False
LocalHorizonModeTrue or False
LocationAltitudeDouble (default, "100")
LocationLatDouble (default, "47.64222")
LocationLngDouble (default, "-122.142")
LocationNameString (default, "Microsoft Research Building 99")
MasterControllerTrue or False
ShowCloudsTrue or False
ShowConstellationBoundriesTrue or False
ShowConstellationFiguresTrue or False
ShowConstellationNamesTrue or False
ShowConstellationSelectionTrue or False
ShowCrosshairsTrue or False
ShowDatasetNamesTrue or False
ShowEarthSkyTrue or False
ShowEclipticTrue or False
ShowElevationModelTrue or False
ShowFieldOfViewTrue or False
ShowGridTrue or False
ShowSolarSystemTrue or False
ShowTouchControlsTrue or False
ShowUTCTimeTrue or False
SolarSystemCosmosTrue or False
SolarSystemLightingTrue or False
SolarSystemMilkyWayTrue or False
SolarSystemMinorOrbitsTrue or False
SolarSystemMinorPlanetsTrue or False
SolarSystemMultiResTrue or False
SolarSystemOrbitColorRGB color (default, "64, 64, 64")
SolarSystemOrbitsTrue or False
SolarSystemOverlaysTrue or False
SolarSystemPlanetsTrue or False
SolarSystemScaleTrue or False
SolarSystemStarsTrue or False
StartUpLookAtInteger (default, "4")

Remarks

This command does not perform any action other than that of changing of the user interface settings.

Return Value

If the call is successful, then the response will contain the string:

<Status>Success</Status>

Example Code

Fly to a location
//
// Sets the property "ShowConstellationFigures" to true.


WebClient client = new WebClient();
string  url = string.Format("http://{0}:5050/layerApi.aspx?cmd=uisettings&ShowConstellationFigures=True", getIP().ToString());
string
 response = client.UploadString(url, "");

See Also


update

The update command specifies that the data attached to this command should be added to the layer.

Remarks

The update command will request that data for a layer be updated, with the following parameters (note that the parameter names are case-sensitive):

Required Parameter
Description
 
&idSpecifies the id number of the layer. 
   
Optional Parameters
Description
Default
&hasheaderSet to true if the data has a header row. The header should be the first row of the data. False
&nameA friendly name to rename the layer. No change
&nopurgeThe sending of an update command will delete events that occur before the start time of any events in the update and that have already decayed. Set this flag to true if the event data should not be deleted.False
&purgeallPurge (delete) all events. False
&showSet to true to show the layer, false to hide it. True and false are not case-sensitive.True

The second string parameter to UploadString should contain a comma or tab delimited string containing the data in a form that matches the heading names of the columns in the layer provided in the new command.

Return Value

The following string will be included in the response if the call is successful:

<Status>Success</Status>

If the call is not successful the following string may be included in the response, or other errors may be returned:

<Status>Error - Invalid layer ID</Status>

Example Code

  //
  // 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);
  }


  //
  // Send an UPDATE command with a flyTo parameter
  //
  string lat, lon, zoom, rotation, angle;
  WebClient client = new WebClient();
  string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=update&id={1}&flyTo={2},{3},{4},{5},{6}&instant=True", getIP().ToString(), layerId, lat, lon, zoom, rotation, angle);
  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);
  }

For an example of the use of this command in the sample application, see flushBufferToWWT.

See Also


version

The version command returns the version number of the running version of the LCAPI.

Remarks

The version number should be used in an application to ensure that the LCAPI features used are supported by the version of the LCAPI running on the client computer.

Return Value

The following string will be included in the response if the call is successful (the version number will be more recent than the one shown below) :

<LayerApi><Version>2.8.21.1</Version></LayerApi>

There are no specific errors returned if the call is not successful.

Example Code

Refer to the getIP utility to see how to extract and check a version number.

See Also


General Parameters

The general parameters can be used with any of the commands.

Remarks

Parameter
Description
&datetimeSets the viewing clock to the given date and time, in UTC format, for example: "1/1/2000 12:02:46 AM". This is the date and time that can be set through the View menu.
Formats (month/day/year):
"1/1/2010 11:00:00 PM"
"1/1/2010 11:30 AM"
"1/1/2010 11 am"
"1/1/2000"
"1/2000"
&timerateThe accelerated time to render the visualization, as a multiple of 10.
&flytoSets the position of the view camera. Requires five floating point numbers separated by commas: latitude, longitude, zoom level, rotation and angle.
  • Latitude is in decimal degrees, positive to the North.
  • Longitude is in decimal degrees, positive to the East.
  • Zoom level varies from 360 (the most distant view) to 0.00023 (the closest view).
  • Rotation is in radians, positive moves the camera to the left.
  • Angle is in radians, positive moves the camera forward.

Optionally there can be a sixth parameter containing the frame to change the view to, which can be one of:
Earth, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Io, Ganymede, Callisto, Europa, Sun, ISS.

&instantUsed with the &flyto parameter, set this to true to specify that the camera should jump to the location, or false that the camera should smoothly pan and zoom to the location.
&autoloopTrue sets the layer manager to auto loop.

Example Code

Fly to a location
//
// Extract the values from the text boxes of a dialog, assuming there are rich text boxes with the names shown below.
//

string flyString = richLat.Text + "," + richLong.Text + "," + richZoom.Text + "," + richRotation.Text + "," + richAngle.Text;

WebClient client = new WebClient();
string url = string.Format("http://{0}:5050/layerApi.aspx?cmd=state&flyto={1}&instant={2}", getIP().ToString(), flyString, richInstant.Text);
string
 response = client.UploadString(url, "");

See Also


 

Utilities

The following utilities are not part of the API, but should be useful in building an application.

See Also


getIP

If the LCAPI application and WorldWide Telescope are running on the same computer, you can use the following utility to extract the IP address.

The getIP function extracts each IP address in turn, then calls the CheckForWWTWebServer function to check that WorldWide Telescope is both running at the given address and is recent enough that the LCAPI is supported.

Get 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;
        }

See Also


getUTCtime

With time series data care should be taken with the timings given in the data file. To convert local times to the recommended UTC time the following function could be used. Note that the local time is taken from the computer System time in the example below, so you may need to add some functionality to support local times that are not the same as your computer System time.

 //
 // 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;
 }

See Also


 

Sample 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.Linq;
using System.Text;
using System.Windows.Forms;

//
// Added References
//
using System.IO;
using System.Net;
using System.Net.Sockets;
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 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");

            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 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;
            }
        }
    }
}
//
// End
//

See Also