How to add a new display, add a new data source, create a new gui.
public ArrayList getDataSources ();
public ArrayList selectDataChoice (ArrayList labels);
Select N DataChoice-s, one for each String label in the given labels list.
that define the java class that implements the control, the categories the control is applicable to, a description, label, etc. The properties attribute is a semi-colon delimited list of name=value pairs. These are passed to the DisplayControl when it is created. The data defined in this xml file is is held in the {@link ucar.unidata.idv.ControlDescriptor} class.
Initialization
So, now that we know a bit about what's what here is a short overview of how all of this
gets glued together. It starts by the IDV reading in an xml file (datasource.xml)
that defines the different DataSource-s available and what kinds of data (files, url)
they manage and a property file (controls.xml) that defines the different
DisplayControl-s that are available
(and the kind of data they are applicable to).
GUI
The IDV creates a DataTree (JTree) to
hold the list of instantiated DataSource-s
and DataChoice-s. The actual GUI is not created by the IntegratedDataViewer class. Rather
it is created by a concrete derived class of IntegratedDataViewer (e.g., DefaultIdv).
This allows for us (in the future) to have different user interfaces.
Loading data
When the user picks a file or url to load the pertinent DataSource is found and instantiated.
When the the DataSource is asked for its list of DataChoice-s some of the DataSource-s
will also automatically add DerivedDataChoice-s to their
list. The DataSource and its list of DataChoice-s are then added to the DataTree.
Each DataChoice has a list of DataCategory-s that describe the flavor of data the DataChoice provides. For each DataChoice the
DataTree uses the first DataCategory in this list to create a hierarchy of JTree nodes (each one
representing a component of the DataCategory) in which to place the JTree node that represents
the DataChoice.
Selecting data
When the user clicks on a particular DataChoice in
the DataTree the list of control.DisplayControl-s
is checked and those DisplayControl-s
which are applicable to any of the DataChoice's DataCategory-s is shown (by default in
a popup menu). When the user selects one of these DisplayControl-s it is
instantiated (through the ControlDescriptor) with the DataChoice and a reference to the
IDV (as a ControlContext interface object).
Displaying data
Typically, when a DisplayControl is created
it may have its own DisplayMaster
(e.g., control.ProfileControl) in its own window
and/or it will want to display some Displayable within a common
window (e.g., control.ThreeDSurfaceControl).
For some cases (e.g., control.TextDisplayControl)
there will be no use of the DisplayMaster/Displayable-s.
The DisplayControl-s don't actually deal with DisplayMaster-s. Rather, they
deal with ViewManager-s which are wrappers
aroung the DisplayMaster.
For common display windows the DisplayControl asks the ControlContext for a
ViewManager through the:
public ViewManager getViewManager (ViewDescriptor viewDescriptor);
method. This uses an instance of the ViewDescriptor
to define what kind of ViewManager is needed. Currently we instantiate and
use only one: MapViewManager.
The DisplayControl uses the DisplayInfo class to hold
a reference to itself, its Displayable and the ViewManager in which the Displayable
is displayed. This DisplayInfo object is also used by the ViewManager to keep
a list of the DisplayControl-s that are being displayed within it. The ViewManager
uses the DisplayControl method:
Component getLegendComponent ();
to retrieve an AWT component to place within its GUI.
This AWT component typically contains a button to popup the DisplayControl's
window and a checkbox for toggling the visibility of the DisplayControl's Displayable-s.
Whew!
Definining the data sources
The set of data sources available to the IDV is defined within the
datasource.xml file.
This file has the form:
data type.factory | The java class name of the DataSourceFactory that is used to create data sources of this type |
data type.label | Text label used within the gui. |
data type.patterns | A set of comma separated regular expression patterns to determine if this data source is applicable for a given file or url |
data type.default_display | Name of a default display control to create when data sources of this type are loaded. |
data type.show_in_tree | Optional boolean that defines if data sources of this type are placed within the gui. |
For example, for files or urls that are textual or html we have the TEXT data source type:
TEXT.label = Text or html files TEXT.factory = ucar.unidata.data.text.TextDataSource TEXT.patterns=.htm$,.html$,.txt$,.text$,http TEXT.default_display = textdisplay TEXT.show_in_tree = falseThis data source type is applicable to anything that ends with .htm, .html, .txt or .text (The $ used in the patterns represents the end of line character in a regular expression.). For this data source the IDV will automatically create a DisplayControl of type textdisplay and the data source will not be placed within the GUI (e.g., within the JTree of DataSource-s).
On initialization the IDV reads in the properties file and a set of
{@link ucar.unidata.util.PatternFileFilter}s are created
which hold the regular expression patterns define in the properties file.
These PatterFileFilters also hold an Object id
attribute
which the IDV uses to hold the String data type name.
This set of PatternFileFilters is used in two ways. The first way is when the user
is selecting a local data set using a FileChooser. The PatternFileFilters
are used as filters for the FileChooser.
Definining the display controls
The IDV reads in the controls.properties file to determine the set of DisplayControl-s
available. This properties file holds a comma separated list
(displays) of a set of display control names, e.g.:
displays=planviewflow, surface, profile, contourxs, contourcolor, planviewcontour, ...These display control names are used to do subsequent lookups in the property file. We use this list (instead of enumerating across the keys of the Properties) so there is an ordering of the display controls when interacting with the user. It is important that when adding new displays to this list there are no line breaks.
Each control name in the list is used to look up the actual DisplayControl class name, its description and the data categories this DisplayControl is applicable to, e.g.:
windbarbplan.class=ucar.unidata.idv.control.FlowPlanViewControl windbarbplan.label=Wind Barb Plan View windbarbplan.desc = Add a wind barb plan view of flow vectors windbarbplan.categories = macro.windvector windbarbplan.properties = windbarbs=true;Here we have a display control with id
windbarbplan
. It is implemented by the
control.FlowPlanViewControl class.
The windbarbplan.categories
entry defines the data categories this
display control is applicable to. Note the value is macro.windvector
.
This value is actually a reference to another entry in the properties file:macro.windvector = *-windvector-*In general, when the IDV deals with property files it uses a macro substitution mechanism (defined in {@link ucar.unidata.util.Misc#getValue}). The value of a property is scanned for anything of the form:
macro.some_name
.
The value of the property macro.some_name
is then used to do a
textual substitution. Any macro's defined in the value of a macro property
are not substituted. The value of the original
property can hold any number of macro strings.
The categories field is a semi-colon (";") delimited set of data category strings. Each data category specification is a dash ("-") delimited set of category components. So a data category can be viewed as a hierarchy or path of components. For example, you could have a categories entry:
textdisplay.categories = text;htmlThis says that the
textdisplay
DisplayControl is applicable
to any data which has data category of text
or html
.
Now lets suppose we know there is data available that is html but has a more
complex categorization, perhaps html that is weather related e.g.:html.weatherIn this case we might have a specialized display control that knows how to deal with these
html-weather
categorized data. In this case we would have
a data category like:special_html_display.categories= html-weatherThis special category would only be applicable to the special "html-weather" data. However, this data is still "html" like data and we would also want the
textdisplay
DisplayControl to be able to display this data. To do this we can take advantage
of the regular expression capability of data categories. Each component of a data
category can also be a regular expression. So we could change the category
of the textdisplay
to be:textdisplay.categories = *-text-*;*-html-*This states that the
textdisplay
is applicable to any data
that has a category that contains "text" as a component. This can be read
as: 0 or more (arbitrary) components followed by a "text" component followed by 0 or more
(arbitrary) components.
2D-TIME-*is read as any category that begins with the components "2D" and "TIME" and ends with 0 or more components.
2D-TIME-+ (2D-TIME followed by one or more components) 2D-TIME-. (2D-TIME followed by one component) 2D-TIME-.-FOO (2D-TIME followed by one component followed by FOO)One could also use richer regular expressions here but it has not been thouroughly tested (and needed) yet. Note: currently a string representation of a category is parsed (in DataCategory) using a StringTokenizer, breaking on "-". So if you use a regular expression that contains a "-" (e.g., [A-Z]+), this will break. Sometime in the future we'll tokenize smarter to allow for escapes, e.g.: [A\-Z]+.
The control data read in from the controls.properties is held in the ControlDescriptor class. This class has methods for testing whether the DisplayControl it represents is applicable to some DataCategory (ControlDescriptor.applicableTo) and for instantiating the DisplayControl (ControlDescriptor.doMakeDisplay).
private String getDataType (String file);
This method iterates through the list of PatternFileFilters
created with the datasource.properties
to find the one that matches the file. If found then the id of the PatternFileFilter
is returned. If no pattern matches the IDV has a special case of a url
that points to an html file but does not match the ".html" pattern.
If the string url begins with "http://" then it is assumed the data source
of of type "TEXT".
Once the data type is found the factory class defined by the properties file is instantiated by the IDV method:
private DataSource createDataSource (Object dataName, String dataType, String globalId);
This method takes a specification of the data that is to be loaded
in, Object dataName
and the data type String dataType
.
The globalId
is used for persistence and may evolve in the future.
The dataName typically specifies a String file name or url but may hold anything
(e.g., a list of images).
The DataSource that handles this data is constructed using a reflection based factory pattern.
The factory class name that creates this data source is found from the properties file.
String factoryClassName = (String) dataSourceProperties.get (dataType+".factory");
Class factoryClass = Class.forName (factoryClassName);
This class must provide a constructor that takes two arguments: a DataContext
and the class of the dataName argument:
Constructor ctor = factory.getConstructor (new Class[]{DataContext.class, dataName.getClass()});
DataSourceFactory factory= (DataSourceFactory) ctor.newInstance (new Object[]{(DataContext)this, dataName});
The DataContext is an interface which the
IDV implements.
The data source is then retrieved:
DataSource dataSource = factory.getDataSource ();
It turns out that so far all of the DataSourceFactory-ies are actually the concrete DataSource
classes (e.g., GeoGridDataSource,
ImageDataSource). These classes all derive
from DataSourceImpl which implements the
DataSourceFactory interface by just returning "this" for the getDataSource method.
Once a DataSource has been instantiated the method
IntegratedDataViewer.loadDataSource
is called.
This method does a couple of things. First it runs through the list of initial paramaters
and displays (defined via the command line with the "-display" argument, see Command line arguments), creating the display for any matching parameters held by the
DataSource.
Next, any default displays defined for that data source in the datasource.properties are created. Finally, the dataSource is added to the DataTree if the "show_in_tree" property for that dataSource (defined in the datasource.properties file) is true.
DataCategory
A data category is hierarchical and can be described as a string:
component1-component2-...-componentNWe use dashes ("-") to delimit each sub-component. An instance of a DataCategory class represents one of the sub-components. A DataCategory has a reference to a parent DataCategory and a child DataCategory. For example, the string FOO-BAR-ZOO is represented by three DataCategory objects:
+-----+ +-----+ +-----+ | FOO |--> | BAR | --> | ZOO | +-----+ +-----+ +-----+The DataCategory class has a number of static utility methods for parsing a string and returning a DataCategory object and for parsing a set of semi-colon delimited categories and returning a list of DataCategory objects.