Professional Documents
Culture Documents
(1.9 release)
http://creativecommons.org/licenses/by-sa/3.0
1.- Introduction
The present manual pretends to be of help for those who would like to develop over
gvSIG. Because of that, and to be more useful the language would be simple and down to
earth.
It is written for developers by developers. That means it is expected the lector is
experienced in java programming.
To begin, gvSIG has been programmed with Eclipse. Other development platforms could
be used, but all the examples in this manual were done with Eclipse (as well as the entire
gvSIG).
Summing up: if you don't know java or Eclipse it is recommended to begin learning them
before you continue with this manual. (Forewarned is forearmed).
Another recommendation: in order to create extensions over an application, it is VERY
recommended to know the application at depth. So, it's very convenient to spend some
time becoming familiar with gvSIG from the user's side. There it is a manual on the web
(http://www.gvsig.org) with more than 300 pages (Spanish for now, but is been translated
to English), and you can use it as a reference manual. There are shorter tutorials showing
the most common options. However, when creating an extension consult the reference
manual and become familiar with its usage.
2.-All are extensions. How to build a project.
Lo primero que vamos a ver es cómo se organiza internamente gvSIG.
The first thing we will look at is how gvSIG is organized internally.
One of the requirements for gvSIG was to be modular, that is, it should allowed for
functionality improvements without having to modify the original code.
That is the reason we start from a nearly empty application that basically give us a
windows manager and a load of plugins based on configuration files. The idea is exactly
the same as the one used by Eclipse. Our plugings are the equivalent to the bundles from
Eclipse, and Andami (our framework) is the equivalent of the RCP from Eclipse.
In a gvSIG installation, the andami.jar contains the base application, and the class that is
executed is Launcher.java. Then we find the extensiones directory, and inside that
directory there are as many subdirectories as plugins installed in Andami.
Those subdirectories have the following structure:
– A file config.xml where all the dependencies for that plugin are specified, the
menus, toolbars and buttons that are available for the application and the
implemented classes defined by the functionalities of the plugin.
– A file xxxxxxx.jar with the classes that make up the plugin (it can be with the file
config.xml, or in some directory, e.g. Lib).
– Other libraries used by the plugin will be next to the .jar file mentioned before,
usually in lib directory.
– Other files used by the plugin (button icons and/or any other file to be used by that
plugin.
Downloading Andami
After downloading every project into your workspace you can switch on the Build
Automatically setting. If everything works fine, you should not see any red crosses on your
projects and you can start your first build.
NOTE: Sometimes Eclipse forgets some referenced libraries, so if you get missing classes
or jars in a project, try to remove any jar of the referenced libraries set and add it again.
There is a main ant task in appgvSIG called install-all that runs every ant of the
different gvSIG extensions and libraries in the correct order and places everything under
Andami. You should also run the ant task of the binaries project to copy the proper native
libraries into that folder.
Configure the run dialog
Now you can configure the run dialog to launch gvSIG. If you have a workspace for gvSIG
1.1.2 the configuration is the same and you can copy the arguments and environment
variables to this new workspace.
The different screens of the New Java Application that should be configured are printed
here for your convenience.
</target>
<target name="generate-without-source"
description="generate the distribution without the source file">
<!-- Create the distribution directory -->
<mkdir dir="${dist}"/>
<target name="move-to-andami">
<move todir="${extensionsDir}/${plugin}/">
<fileset dir="${dist}" includes="**/**"/>
</move>
</target>
<target name="clean"
description="clean up" >
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${dist}"/>
<delete dir="${build}"/>
</target>
</project>
Inside the file will be defined the directories that are going to be used and where the
results of the compilation will be saved. The most important variables are:
• ${extensionsDir} => The directory where we will copy everything.
• ${plugin} => The name of the plugging.
In the file you could see the targets defining it: to initialize, to erase the temp folder, to
copy the files with the button icons, the config.xml file, etc.
The bold text is what you usually will need to change (the name of your pluggin and the
classes your are including in the .jar, basically.
The easiest way to start a new gvSIG plugging is to copy this example, verify it compiles
and works and then make the necessary changes to the package's name, the config.xml
and the build.xml files. Then, the best thing is to look if there is any functionality already
implemented within gvSIG that it is similar to the one we would like to implement. Probably
that would be the case (click in the map, query a layer and display a form with the selected
data, add a layer by code, add a table, open a project, zoom to selected, ...).
Once the common behaviors of our pluggins are identified, it is time to investigate which
extensions within gvSIG can perform those tasks. We will see this with more calm later on,
in the appgvSIG description, but for now it is enough to know that the starting point for our
search is the config.xml, where we will find the menu or button and the class that gets
executed upon selection of the option that we would like to use as a model.
3.- Andami and the management of windows
The application or framework that serves as skeleton for gvSIG is _fwAndami
(framework Andami). It has several functions:
• To send the application.
• To load plugins and its extensions.
• To manage the translations.
It offers services to the pluggins for the management and load of dependencies, handling
of windows, for some utilities to send tasks into the background, etc.
The common libraries for the entire application is in the directory lib. Of special interest is
log4j (registry of incidences), xerces (management of xml), castor (it is used to read xml of
the project), gvsig-i18n (translations), beans (common graphic components), iver-utiles
(generic useful classes).
Within Andami we find a series of classes that can be of utility for extensions. For
example:
The class com.iver.andami. Launcher.java is the entry point to the application. In spite of
being quite complex, when an extension sends an unexpected exception, it can
sometimes easily be solved by adding a breakpoint to watch the class.
While working with our own extensions, we often needed to use the class
com.iver.andami.PluginServices. As its name indicates, it is a class that yields service
plugins, offering static methods like:
• getMainFrame () => the main application window
• getMDIManager () => through this class, we access the active window
(PluginServices.getMDIManager () .getActiveWindow ()), something that we will
continously use in the methods isEnabled and isVisible of all the extensions. It is
also used to add a new window, and a few other minor things.
• getDlgPreferences () => obtain the common dialogue of preferences, where all
plugin configurations are registered.
• getArguments () => recover arguments by command lines. For example, it is very
useful for opening a project by line commands.
• getExtension (Class) => with this call, you can obtain an instance of the extension
that executes that class. If the call is made in the initialize () method, you must be
sure the extension has already been initialized (careful with the dependencies).
• getText (Object, String) => is used to work with translated text. From the plugin and
a key, you obtain the string in the current language.
• registerKeyStroke (KeyStroke) => serves to determine the fast access global keys.
Additional methods exist, but the most frequently used are those we have described.
Andami is also responsible to manage a theme by default with the controls and the generic
aspects of the application, to launch the welcoming image of the application (Splash
Window) and other tasks we will not go into detail since you normally will not need to
manipulate the code there.
The windows in Andami behave like generic units. That is, from the start, they do not
depend on a Swing class. The reason is someday it may be decided to change this aspect
of the application completely.
The plugin that instantiates the windows is the project libCorePlugin. In this plugin, the
extensions are defined which connect the menu, control and window definitions with actual
Swing objects. In our case, we chose to use the JInternalFrame class as the basis for
gvSIG windows. Being this is an MDI (Multiple Document Interface) application, it seems
most suitable.
The windows in Andami are defined thanks to the IWindow interface. This interface is very
simple, it only requires to implement the getWindowInfo () method. This returns for a
WindowInfo class where it is specified the size of the window, the title, and several
constants indicating if the window is modal or not, resizable, or a pallette (very useful for
showing information, a group of controls, or tools for example - it is always drawn over all
other windows).
In order to show how to add a window in Andami, it is best to see an example:
PluginServices.getMDIManager().addWindow(symbolPanel);
where symbolPanel is
We see that windows in gvSIG are in fact subclasses of JPanel implementing the
IWindow interface.
When we do not want to re-create a window again, but would rather reuse an already
created window, we use the ISingletonWindow interface. This interface add a method
(getWindowModel ()) that it is used for avoiding the creation of new windows if they have
been previously created. That way it is avoided the creation of multiple windows with each
requesting information click.
and getWindowModel:
The windows currently in gvSIG are defined in libCorePlugin (the WindowInfo definition is
used to create JinternalFrame windows). Sometimes it is useful to know in more detail how
a window was created (to distinguish between SingletonWindow and other types, etc?).
You can put a breakpoint in the class com.iver.core.mdiManager.NewSkin.java, in the
function addWindow. rBe careful with this, future versions libCorePlugin might be changed
to create a different type of window (this has not been planned, but is a possibility).
4.- Internal Libraries. FMap.
We have tried to create libraries that are reusable outside of gvSIG. There are many
libraries that can be used in such a way, and that can help with many of the problems a
programmer can find in the Geographic Information Systems (GIS).
Among those libraries, there is one with the higher hierarchy for the programmer since it is
the starting point for the majority of the libraries within gvSIG.
FMap can be seen as the graphic engine of gvSIG. It is the library that manages the layer
collection gvSIG can work with and it is in charge of drawing those layers in a component
(MapControl) in which the user can interact with.
Within the library there are some templates or typical tools defined (Behavior) with the
usual user-map interaction behavior (look in the tools package).
For example, the behaviors of:
• Click in the map and execute an action based on the clicked point (show
information) => PointBehavior.
• Move the mouse and execute an action based on the coordinates (show on the
status bar) => MouseMovementBehavior.
• Draw a rectangle and execute an operation with it (select by rectangle) =>
RectangleBehavior.
• Click, move the mouse and realese (e.g. the pan tool) => DraggerBehavior.
• PolylineBehavior, PolygonBehavior, CircleBehavior => They allow the user to
draw polylines, polygons or circles and they provide feedback to the user (the user
sees how he draws these geometries).
These behaviors only launch events. In reality, the code executing what the user really
wants is located in the listener classes. You can take a look to the classes PanListener,
PointListener, CircleListener, etc.
The reason for this separation is that there are common behaviors to many of the tools.
For instance, we can draw a rectangle and then added as a graphic in the map, or we can
use the rectangle to execute a queryByRect over a layer.
As for the way to use it:
//Select by polygon
PolygonSelectListener poligSel = new PolygonSelectListener(m_MapControl);
m_MapControl.addMapTool("polSelection", new Behavior[]{new PolygonBehavior(poligSel),
new MouseMovementBehavior(sbl)});
The adequate listener is created and it is associated with a particular Behavior with the
addMapTool method from MapControl. The String “polSelection” is used internally to
select the tool:
} else if (actionCommand.equals("SELPOL")) {
mapCtrl.setTool("polSelection");
}
The most used classes in FMap (or at least in our opinion) are:
• MapControl. This is the component that shows the user the map, and over which it
interacts with its tools. The most important method is getMapContext(), that returns
objects of type MapContext.
• MapContext. This is the MapControl model. With it we have access to the
collection of layers (vector, raster and of any type) and the object ViewPort, that
defines the visible extent, the coordinate system being used, the projection, and
some methods very useful to transform between the screen coordinates (pixels) and
the real world coordinates (meter, km, etc.)
• FLayer. This is the common interface to all layers. It is the root to a hierarchical
structure defining the types of layers that we will be working with. In FIGURE XXXX
we could see this structure. The vector layers are FLyrVect and the raster layers
are derived from FLyrRaster.
• The access to the vector data of a layer is done through the ReadableVectorial
interface (FLyrVect.getSource()). With that we have the necessary methods to
search through the features of a layer.
• IFeature => Geometry + alphanumeric data.
• SelectableDataSource (FLyrVect.getRecordset()). It offers access to alphanumeric
data with the option to select records (getSelection()) and obtain information related
to the fields of the associated table. It is also the internal model of any isolated
alphanumeric table, not associated to any layer.
Layers are created (not mandatory) from the layer factory that joins the drivers (low-level
access to the entities in the files or spatial database) with the vector layers and/or their
legends by default. Example:
The low-level access to the data is based on a drivers system. The location of those
reading drivers is specified with the call LayerFactory.setDriversPath(String path). This call
needs to be done upon initialising the application.
We will also find in FMap the drivers needed for working with layers of different formats,
especially those that are vector, since originally FMap was created as a library to manage
vector data.
In gvSIG, the drivers should be in the gvSIG/extensiones/com.iver.cit.gvsig/drivers
directory. There it should be found the drivers to access shp, dxf, dgn, dwg, mysql,
postgresql, oracle, gml, etc.
The drivers are managed by the libDriverManager library. The condition a driver must
comply is defined by the Driver interface, that it only requires a name for that driver (to find
it in the registered driver collection (getName()). Curiously enough, there is an additional
strange condition: the classes that are drivers must be named WhatEver**Driver**.java.
That is, they should end in the word Driver.
That is all as for the data reading. Data writing follows a similar structure. There is an
interface IWriter that inherits from Driver and that defines de writing methods. We will
come back to data edition later, since it is a very complex subject.
The union of the layer with the driver is done according to the pattern Adapter. There are
internal classes that can adapt the driver's methods to the requirements of a layer reading.
The package com.iver.cit.fmap.drivers contains the classes and the interfaces for data
reading. To be revised: ITableDefinition, ILayerDefinition, VectorialDriver and their
derived interfaces: IFeatureIterator, MemoryDriver and ConnectionFactory.
A typical question that shows up once in a while:
Where can I see the code that draws gvSIG?
There is no a simple answer. In addition, the symbology part of the next gvSIG versions is
going through a spectacular change, thus anything it is said here may not be valid for the
next version. However, let?s concentrate on the areas that will not change.
We have said that MapControl is the component that shows the map and the one the user
interacts with. However, MapControl is not responsible for drawing the map, it manages
the drawing requests and it keeps a Timer that allows to refresh the component every
once in a while so the user sees the map drawing. The real drawing DOES NOT happen
over the MapControl Graphics. In fact, it is MapContext the one drawing in the background
over a graphic that MapControl has obtained from a BufferImage.
So the quick answer is that what we see it is happening in a separate thread, so the user
will not notice the interface slowing down and that it is drawn over an image in memory.
If we look at the MapContext code, we will find the following method:
prepareDrawing(image, g, scale);
long t1 = System.currentTimeMillis();
layers.draw(image, g, viewPort, cancel, scale);
//layers.setDirty(false);
long t2 = System.currentTimeMillis();
System.err.println("Tiempo de dibujado:" + (t2 - t1) +
" mseg. Memoria libre:" + Runtime.getRuntime().freeMemory() /
1024 + " KB");
/*
* Free resources
*/
System.gc();
}
At least there are 3 important things to stand out.
The first one is the object Cancellable. It is used for interrupting the drawing, and within
the layer extent, it verifies if the object returns true (it has been cancelled) while it is
drawing the geometries. If that is the case, it has to get out of the geometries extent. This
is important if anytime we would like to implement a different drawing strategy to the ones
already implemented and we would like to keep the option of interrupting the drawing. If
we do not want the interruption, we could pass a Cancellable object to this method that will
always return false.
LayerFactory.setDriversPath(
"C:\\eclipse3\\workspace\\Andami\\gvSIG\\extensiones\\com.iver.cit.gvsig\\drivers");
FLayer l = LayerFactory.createLayer("Vias",
(VectorialFileDriver) LayerFactory.getDM().getDriver("gvSIG shp driver"),
new File("C:/vias.shp"),
CRSFactory.getCRS("EPSG:23030"));
newMapControl.getMapContext().getLayers().addLayer(l);
((ConnectionJDBC)conn).getConnection().setAutoCommit(false);
Statement st =
((ConnectionJDBC)conn).getConnection().createStatement(ResultSet.TYPE_SCROLL_INSENSITIV
E, ResultSet.CONCUR_READ_ONLY);
rsGood = st.executeQuery("SELECT NOM_PROVIN, GID FROM " + dbtable
+ " ORDER BY GID");
driver.setData(conn, lyrDef);
}
catch (Exception e){
e.printStackTrace();
}
It is necessary to notice a vector layer is defined normally based on a driver and an object
ILayerDefinition, where there are specified the layer?s name, fields, coordinate system,
etc.
Point2D p = event.getPoint();
Point2D mapPoint = mapCtrl.getViewPort().toMapPoint((int) p.getX(), (int) p.getY());
// Tolerancia de 3 pixels
double tol = mapCtrl.getViewPort().toMapDistance(3);
FLayer[] actives = mapCtrl.getMapContext()
.getLayers().getActives();
for (int i=0; i < actives.length; i++)
{
if (actives[i] instanceof FLyrVect) {
FLyrVect lyrVect = (FLyrVect) actives[i];
FBitSet oldBitSet = lyrVect.getSource().getRecordset().getSelection();
FBitSet newBitSet = lyrVect.queryByPoint(mapPoint, tol);
if (event.getEvent().isControlDown())
newBitSet.xor(oldBitSet);
lyrVect.getRecordset().setSelection(newBitSet);
}
}
SelectableDataSource ds =lyrVect.getRecordset();
int idField = ds.getFieldIndexByName("NOM_PROVIN");
FBitSet bs = lyrVect.queryByPoint(mapPoint, tol);
for (int i=bs.nextSetBit(0); i >=0; i=bs.nextSetBit(i+1))
{
long idRec = i;
String nom = ds.getFieldValue(idRec, idField).toString();
dlg.setProvinName(nom);
}
Here we have selected the province that contains the point ?mapPoint? and we have
search through the selected records, recovering the field value ?NOM_PROVIN?.
We will see more examples in chapter 6.
5.- gvSIG
In this chapter we will see how the gvSIG application is organized internally. We will start
with the main body (appgvSIG) and we will see how its functionality has been extended
based on extensions.
In fact, appgvSIG is in itself another of Andami's plugging, containing the extensions that
defines the basic functionalities of gvSIG.
gvSIG is internally organized similarly as it is organized externally. That is, there are
classes controlling the gvSIG project and the project is composed at the same time by
various types of documents (Views, Maps, and Tables) although they can be extended by
other types of documents.
The packages are organized according to this structure:
NOTE: The structure could seem somehow arbitrary (in fact, it is :-(). But it is the one
available, and we will try to describe a little the packages. In the future they shall be re-
organized.
• The classes implementing IExtension are in the root package: com.iver.cit.gvsig.
This is something that has been followed by the developers and it seems to be a
good practice. This way it is easier to find which are the entry points for the options
in the menus, buttons, etc.
• The package com.iver.cig.gvsig.project is a good entry point to get acquainted
with gvSIG. In theory, gvSIG follows a structure which is influenced by the
organization by documents within a project. The most important classes here are
Project.java and ProjectFactory.java. The first one represents the gvSIG project
and the static method createFromXML() is the entry point to see how to read a .gvp
file (gvSIG project). The contrary step, create a .gvp file is done with the
writeProject method of the ProjectExtension extension and it calls the
getXMLEntity() method from the Project class.
• Within the project package is the com.iver.cit.gvsig.project.documents package.
We will find there the ProjectDocument and ProjectDocumentFactory classes. The
first one is the class from which the rest of the documents inherit (can be selected
and use the F4 key). The second is the one responsible for creating the documents
and manage the potential documents that in the future could expand the model of
objects, for example with 3D views.
In the rest of the packages we will find the classes related with the layout, view and table.
Within contextMenu the context menus and in the other packages some exceptions and
the user interface classes to add layers (GUI)
The rest of the packages do not follow a set logic or at least it is not apparent. Surely they
will be reorganized in the future. However, in case you ever need to look for code, it is
possible you could be interested to look at com.iver.cit.gvsig.gui.layout. Within this you
will find the Layout class, responsible for the maps prepared for printing and the classes
within the fframes package, were you could see all that can be added to a layout (views,
legends, north arrows, text, graphics, scale, etc). This area is waiting for a rebuild that will
allow for easy expansions from external plugging?s.
An interesting class to begin to explore when you are working at this project level is the
com.iver.cit.gvsig.ProjectExtension class. This is the entry point to the operations of
creating a new project, loading an existing one, save it, etc.
Once introduced (although briefly and superficially) the packages structure, let us take a
closer look at the principal parts of gvSIG. Or at least those you would work most likely.
5.1.- Views
The default view in gvSIG (the one everyone is used to see) basically contains a
MapControl object (where the layers are drawn), a MapOverview object (the locator which
inherits from MapControl) and a table of contents (ToC) where the user can see the
available layers, their drawing order, etc.
In the 3D extension, a new type of view has been created (3D) and in doing so it has been
implemented a mechanism so different types of View can be defined.
5.1.1.- View.
A view relates to the project through the class
com.iver.cit.gvsig.project.documents.ProjectView. The object
com.iver.cit.gvsig.project.documents.view.gui.View is created with the method
createWindow() and the model is assigned to it. The model is the ProjectView document
that has a MapContext associated to the MapControl and another MapContext that
corresponds with the layers of MapOverview. The ToC is in reality another way to view the
MapContext associated to the MapControl.
View has also a console that is used when a layer in being edited, so the user can key in
the editing commands as in a CAD software.
5.1.1.1.- MapControl
In many extensions the first thing is to obtain the object View and from that to obtain the
associated MapControl and MapContext. This is usually the entry point. With the
MapContext we have already the list of layers (Flayers), search tool for the layer that we
would like to work with, etc. Let's take a look at an example:
com.iver.andami.ui.mdiManager.IWindow view =
PluginServices.getMDIManager().getActiveWindow();
if (!(view instanceof View))
return;
View vista=(View)view;
IProjectView model = vista.getModel();
MapContext mapa = model.getMapContext();
MapControl mapCtrl = vista.getMapControl();
logger.debug("Comand : " + s);
if (s.equals("FULL") ) {
try {
// mapa.beginAtomicEvent();
mapa.getViewPort().setExtent(mapa.getLayers().getFullExtent());
mapa.clearAllCachingImageDrawnLayers();
vista.repaintMap();
// mapa.endAtomicEvent();
} catch (DriverException e) {
NotificationManager.addError("Error obteniendo el full extent",
e);
}
}
This portion of code comes from the com.iver.cit.gvsig.ViewControls class. It uses
PluginServices.getActiveWindow() to obtain the active window. If that window is of type
View, then it gets the MapContext to show a zoom of all the layers:
mapa.getViewPort().setExtent(mapa.getLayers().getFullExtent());
The most interesting classes to review to see examples of this type are:
• ViewControls.- They are many commands in only one class: full extent, panning,
configure locator map, zoom in, zoom out, meassure, set layers as visible, not
visible, active, not active, save to raster, change scale...
• ViewSelectionControls.- Select by point, by rectangle, by polygon, by layer and
invert selection.
• Export.- Exports to raster what is visible in a view.
• AddLayer.- It is used for adding layers to a view. It is far more complex of what it
looks like, since it shows a dialog box extensible from external pluggings. In fact, it
is used by other pluggings to add layers based on spatial databases, WMS, WFS,
etc. In this class there is only the necessary to add layers based on files (.shp,
raster, .dxf, .dgn, etc). It is also important that you pay attention to the projection
treatment associated to the layer and the view. If they do not coincide, it will try to
reproject on the fly.
• InfoToolExtension.- The window to show information is far more complex of what it
looks like (perhaps far more than necessary). This class is only the entry point to
find out its functioning.
Within the views it is very interesting to see the relationship between the MapOverview
and the MapControl. We will see it next.
5.1.1.2.- MapOverview
A MapOverview is a subclass of the MapControl, with the particularity that is linked to the
MapControl within the View document. By default it is on the lower left corner, and it allows
for any kind of layer to be loaded onto it and for the management of its legend. It is usually
used as a locator, showing some location layers and a rectangle that shows the visible
rectangle of the associated MapControl.
The MapOverview is a listener of the MapControl, so that any change in the visualisation
of an area makes the MapOverview to update and show the corresponding area shown by
the MapControl.
The code responsible of this behaviour is:
repaint();
}
g2.setColor(Color.red);
g2.draw(extent);
g2.setColor(new Color(100,100,100,100));
g2.fill(extent);
// drawing vertical and horizontal lines
Point2D pRightUp = vp.toMapPoint(getWidth(), 0);
g2.setColor(Color.darkGray);
g2.draw(linVert);
g2.draw(linHoriz);
g.drawImage(image,0,0,this);
g2.setTransform(new AffineTransform());
extent=null;
}
/**
* <p>Called when the background color of the view port has changed.</p>
*
* @param e a color event object
*/
void backColorChanged(ColorEvent e);
/**
* <p>Called when the projection of the view port has changed.</p>
*
* @param e a projection event object
*/
void projectionChanged(ProjectionEvent e);
}
The method execute() gets as parameters the ToC item over which we had clicked and the
list of selected layers. In any case, the method more useful is getMapContext(), since it
gives access to the real model of what we are looking at in the view (Layers + ViewPort).
The TOC class (com.iver.cit.gvsig.project.documents.view.toc.gui) is a panel containing
the tree of layers and the main component inherits from a JTree => DnDJTree (it is added
a support for drag and release, among other things).
The items representing the layers follow a Composite pattern, where the branches
represent the layers and the groups of layers and the leaves represent the symbols with
their corresponding descriptions. Visually they are an icon + a label.
5.2.- Tables
Tables in gvSIG are of two types: those showing the alphanumeric data associated to a
layer in a View and those that are added to the project as standalone tables with
supported formats or coming from any database with supported connexion by gvSIG
(ODBC, memory process, etc).
The table of the layer is related to that layer and it has a series of properties and
associated event listeners to allow behaviours such as that selecting on the layer is
reflected on the table and vice versa. They have been added to the project by the Show
attributes of active layer button that is in the View and that becomes enabled when there is
an active layer.
The class representing the document Table is ProjectTable. It is basically a bundle around
an object IEditableSource that is where the real data is taken from (values of rows). The
rest of the properties are used to manage the layers associated to that table, the visible
fields, the aliases of those fields, etc.
Usually we will work with the model IEditableSource that is under, if we want to work with
the raw data.
If we need to add functionalities to the table window, than we will have to look at the
com.iver.cit.gvsig.project.documents.table.gui.Table class. This class is the one
implementing the visible part of the Table document. It is very complex and probably
subject to change in the gvSIG version 2.0, thus we are not going to go too deep, just a
little over it.
• It has a series of associated listeners to answer to the mouse and keyboard events
(TableKeyListener, MouseRow, MouseHandler).
• It also answer to the selection events (TableSelectionListener).
• There are methods for editing (columns, rows, cells) that are very related to the
internal model of the table (accessible through getModel().getModelo() =>
getModel() it returns the associated ProjectTable and ProjectTable.getModelo()
returns the IEditableSource interface).
Up to the date the classes implementing IEditableSource are EditableAdapter and
VectorialEditableAdapter, which keep the changes in memory until the end of the editing
session and all data is saved. This system presents some inconveniency and because of
that, it is likely to be changed in version 2.0, so we will not go into much detail about it.
5.2.- Layouts
The last document we will see is Maps or Layouts.
This is the one used for creating printouts and even though it is one of the most useful
components (and sometimes complex) of gvSIG, it is very easy to explain.
The Layout object (com.iver.cit.gvsig.gui.layout.Layout) is basically a canvas over
which a hierarchic structure of FFrames is painted.
FFrames is an abstract class representing something that can be added to the Layout. It
has defined limits (a rectangle) and each class inheriting from FFrame is responsible for
drawing within that rectangle. This way the door is left open for the type of elements that
can be added to a Layout
To see the entire hierarchy we can open the IFFrame interface (within the
project.documents.fframes package)and select F4. We will find there all that can be added
to the Layout:
• FFrameView: It is the most complex. It represents a View. There are few properties
that can be defiend. (see user?s manual) such as if it is linked (or not) with the view
that generated the object, the desired printing scale, etc. It also supports the
functionality to change the zoom and the scale using tools from within the layout
(very useful to frame correctly before printing).
• FFrameScaleBar: A scale bar (usually link to a FFrameView) with lots of
customization options.
• FFrameLegend: The map legend, with the layers and their symbology. It can also
be customized, to indicate which layers can be visible or invisible.
• FFramePicture: To add images.
• FFrameNorth: A particular case of FFramePicture to add north symbols (usually
based on a SVG files and rendered by the Batik library).
• FFrameGraphics: Represents the vector elements available to be added to a map,
such as boxes, break lines, circles, curves, etc.
• FFrameText: Text with support for font, colour, size.
All these graphic elements (and the ones to come) can be modified and the user can
interact with them through dialog boxes available in the
com.iver.cit.gvsig.project.documents.layout.fframes.gui.dialogs package.
Normally, it will not be necessary to touch any of these, but it is possible that during the
creation of automated cards or series of layouts using code, we may have to retouch some
of these elements (the typical thing is to change the FFrameView extent).
This is an example of code that iterates for the FFrame:
IWindow w= PluginServices.getMDIManager().getActiveWindow();
if (w== null) {
return false;
}
if (w instanceof Layout) {
Layout layout = (Layout) w;
IFFrame[] fframes=layout.getFFrames();
for (int i = 0; i < fframes.length; i++) {
IFFrame f = fframes[i];
if ((f.getTag() != null) && (layout.showIconTag())) {
f.drawSymbolTag(g2);
}
}
}
getTag() makes reference to a tag that can be added to all the instances of a FFrame. Its
purpose is to be able to identify within a plan those FFrame that we would like to work
with, for example, we can mark the fields of a form with the table and field that we could
use to substitute the text in each printing of a series of cards.
6.- Examples
A series of examples (with explanatory commentaries in the code) have been developed
with the manual and are distributed together. We will describe them separately here
briefly.
6.3.- Editing
In this example it is shown how to use a tool already built in another pluggin (extCAD) to
inherit from its behaviour and to modify or expand it. In short, it has been implemented a
polygon autocompletion feature. The user can draw a polygon and if he/she does it over
another existing polygon, the tool takes it into account to follow the boundary between the
two plolygons.
The files involved are:
• config.xml.- The extension is defined with the corresponding button and toolbar.
• MyInfoExtension.java.- The class that creates and assigns the tool listener.
• tools/AutoCompletePolygon.java
Please, pay attention in config.xml to the lines
<depends plugin-name="com.iver.cit.gvsig"/>
<depends plugin-name="com.iver.cit.gvsig.cad"/>
The package com.iver.cit.gvsig.cad is another plugin, and we want to use some classes
from this plugin. So, our plugin “depends” on the other.
And of course, many of this points will be changed when the new release 2.0 will be out. At
the time of this writing, there are good tutorials here:
http://www.gvsig.org/web/docdev/gvsig_desktop_2_0/
but they are still in Spanish. If somebody wants to help in the translation, he's wellcome :-).
In general, 2.0 is an strong refactoring of the core of gvSIG. The most interesting points:
– A new way of building the project, based in Maven.
– New core geometries
– New Data Acces Library (DAL), more suitable to geo databases access.
This is it! Happy coding and good luck with the commits ;-)