Re: Visualization of realtime data in realtime

Hi Ethan,

> Well, from what I've read, VisAD is very useful in displaying data in
> different formats such as netCDF. But is it possible for VisAD to visualize
> realtime data?
> 
> Let me elaborate.
> I'm dealing with financial data here, and i've got historical data stored in
> a .nc file. I'll need to visualize that, as well as new data records that
> I'm collecting every minute.
> 
> A straight forward method is simply to write the new data to the .nc file
> and load it again. But is there an alternative where I can update the data
> in memory and write out to disk at hourly intervals instead? (the historical
> data can be huge and new data may arrive seconds apart).

Yes, this should not be difficult.  In fact, there are a
number of different ways to do this, depending on what you
want.  For example, the visad.aune.ShallowFluid program
(distributed with VisAD, but only runs on Sparc Solaris)
creates displays of the state of a 2-D atmosphere that
evolves over time.  However, it only keeps the latest
state, rather than a time history.

So I'll take a guess at the way you might want to manage
and display your data.  I'll assume that your data are a
time series of records, where each record is a collection
("Tuple" in VisAD language) of numerical values. So lets
say the MathType of the time series is:

  (RealType.Time -> (value1, value2, ..., valueN))

where value1, ..., valueN are RealTypes.  They might be
share prices, exchange rates, or whatever magic you guys
are working with.

Then you read a .nc file with this data by:

  Plain plain = new Plain();
  FlatField data = plain.open("filename.nc");

Now you want to extend the set of times in this data to
make room for your real-time data.  I'll assume that your
data are sampled on equal time intervals so getDomainSet()
will return a Linear1DSet (the alternative would be a
Gridded1DDoubleSet for unevenly spaced time samples).  So
here's how to extend the time sampling:

  // construct a new Linear1DSet of time samples that
  // extends current set up to "new_last" time in future
  Linear1DSet set = data.getDomainSet();
  double first = set.getFirst();  // first time
  double step = set.getStep();  // time step between samples
  double last = set.getLast();  // this could be the last time
  MathType set_type = set.getType();
  double new_last = ... // new last time
  int new_length = 1 + (int) ((new_last - first) / step);
  Linear1DSet new_set
    new Linear1DSet(set_type, first, new_last, new_length);

  // construct new_data that combines time series from .nc
  // file with space for real-time records
  //
  // copy any range Units - this should not be this complex
  Unit[][] long_units = data.getRangeUnits();
  Unit[] units = new Unit[long_units.length];
  for (int i=0; i<long_units.length; i++) units[i] = long_units[i][0];
  // get MathType of data
  FunctionType time_series_type = (FunctionType) data.getType();
  // construct new FlatField
  FlatField new_data
    new FlatField(time_series_type, new_set,
                  null, null, // no RangeCoordinateSystems from Plain
                  field.getRangeSets(), units);
  // now copy "range" dependent values and fill with missing
  float[][] values = data.getFloats(false); // don't copy
  int dimension = values.length;
  int length = values[0].length;
  float[][] new_values = new float[dimension][new_length];
  for (int d=0; d<dimension; d++) {
    System.arraycopy(values[d], 0, new_values[d], 0, length);
    for (int i=length; i<new_length; i++) {
      new_values[d][i] = Float.NaN; // mark "future" values as missing
    }
  }
  new_data.setSamples(new_values, false); // don't copy

Now you can construct a display and link it to new_data, something
like (I'll assume that you'll use Java3D graphics):

  DisplayImplJ3D display = new DisplayImplJ3D("financial data");
  display.addMap(new ScalarMap(RealType.Time, Display.XAxis));
  // construct maps of various financial values to YAxis, ZAxis,
  // Red, etc
  display.addMap(new ScalarMap(value1, ...));

  // construct a DataReference to link new_data to display
  DataReferenceImpl ref = new DataReference("time series");
  ref.setData(new_data);
  display.addReference(ref);
  JPanel panel = ... // construct a JFrame and GUI
  panel.add(display.getComponent()); // add display to GUI

Now, as your time series data come in, you can add them to
new_data as follows:

  // read values for next record from real-time data
  double val1 = ...
  double val2 = ...
  ...
  double valN = ...
  double[] values = {val1, val2, ..., valN};
  // get MathType of record
  RealTupleType record_type
    (RealTupleType) time_series_type.getRange();
  // add record to new_data
  RealTuple record = new RealTuple(record_type, values);
  int index = ... // ordinal index of current time sample
                  // in new_set
  new_data.setSample(index, record);

Each time you call setSample() this will trigger the
display to update and add the latest record.

Note that the time associated with "index" will be:

  first + (index - 1) * step

where first is in seconds since some epoch (default is
1970-01-01 00:00:00Z) and step is in seconds.  You can
use the DateTime class to help with time calculations.
Also note that you may eventually overflow the time span
of new_data, when you get real-time records for times
later than new_last.  If that happens, you can of course
extend the time sampling of new_data in just the same way
that new_data extends the time sampling of data.

Finally, after you have collected a number of real-time
records and you want to save the combined history and
real-time data to a file, you can do:

  // note "true" is to replace any existing file named
  // "newfilename.nc"
  plain.save("newfilename.nc", new_data, true);

Please let me know if you have any questions or problems.

Cheers,
Bill
----------------------------------------------------------
Bill Hibbard, SSEC, 1225 W. Dayton St., Madison, WI  53706
hibbard@xxxxxxxxxxxxxxxxx  608-263-4427  fax: 608-263-6738
http://www.ssec.wisc.edu/~billh/vis.html