This is an old version of LandSerf. For the latest release please visit www.landserf.org

Chapter 4. Using the jwo.landserf.process classes

By the end of this chapter you will be able to:

4.1 Collecting input and output in a GISFrame

We have already seen how to read, create and write raster and vector spatial objects using Java and the LandSerf classes. When writing your own programs it is likely that you are doing so because you wish to perform some processing of those spatial objects between the reading and writing stages. You can take advantage of much of the processing functionality of LandSerf by creating objects from the jwo.landserf.process package. Classes in this package handle tasks that involve some significant (often time consuming) analysis and processing operations.

Different processing operations will require a range of spatial objects for input, and may create many different output spatial objects. For example, surface peak detection requires a DEM as input, and creates up to three raster maps and one vector map as output. Other routines such as fractal surface generation require no input spatial objects at all. To handle the varying input and output demands of different processes, all the classes in this package use a jwo.landserf.gui.GISFrame to hold the potential collection of input and output spatial objects. The GISFrame is an interface that defines the minimum requirements for assembling spatial objects, often in the form of a graphical user interface. For example, jwo.landserf.gui.GUIFrame is the main GUI window that implements a GISFrame and is used when you normally start LandSerf.

As a programmer, you must decide whether to create a graphical user interface for your program or whether it is to run from the command line. If you do create a GUI, should it use the 'normal' LandSerf GUI or do you wish to create your own GUI? Depending on what you choose, there are various incarnations of GISFrame you can use:

Type of user interface GISFrame to use
LandSerf GUI
'LandSerf' GUI
jwo.landserf.gui.GUIFrame
Customised GUI
Customised GUI
Inherit jwo.landserf.gui.SimpleGISFrame or jwo.landserf.gui.GISFrameAdapter
Command line interface
Command line interface
Inherit jwo.landserf.gui.SimpleGISFrame

The easiest to use is probably the SimpleGISFrame that implements the spatial object storage methods allowing any number of raster maps or vector maps to be added and removed. We will use this class in the discussion below. The GISFrameAdapter is simply an 'empty' implementation of the GISFrame that contains no functionality, but speeds up the process of creating a class that implements the interface. This is only really necessary if you are creating a customised user interface that behaves in a very different way to the one used by LandSerf.

Spatial objects can be added to a GISFrame by calling the addRaster() or addVectorMap() methods. Raster and vector maps can be added as primary (default), secondary or unselected objects. This distinction is important in determining which objects are selected by the processing classes. Most operator on the currently selected primary object, while some require both a primary and secondary selected object. Output from the processing classes can be as new primary, secondary or unselected objects stored in the GISFrame.

You can consult the Landserf 2.2 API documentation to find out what input is required and output produced by each of the processing classes. You may also find the table below a useful summary.

Processing class Input objects Output objects
CombineThread Primary and secondary raster or primary and secondary vector Unselected raster or vector
ContourThread Primary raster Primary vector
DeleteThread Objects provided to constructor None
DemToTinThread Primary raster Primary vector and optional secondary raster (error map)
DispThread Primary spatial objects and optional secondary raster None
FracSurfaceThread Empty primary raster Primary raster
FuzzyFeatureThread Primary raster 6 unselected rasters
OpenThread Objects provided to constructor Primary spatial object(s) and optional unselected objects (if more than one raster or vector provided)
PeakClassificationThread Primary raster Secondary raster (peaks) and optional primary vector (summits) and optional unselected rasters (fuzzy peaks and peak hierarchy)
PitRemovalThread Primary raster Secondary raster (pitless DEM) and unselected raster (pits map)
PointDensityThread Empty raster and (points) vector provided to the constructor Primary raster
PolySurfaceThread Primary raster Primary raster
ProjectionThread Primary raster or primary vector Primary raster or primary vector
RectifyThread Raster provided to the constructor Primary raster
SaveThread Primary raster or primary vector None
ScaleParamThread Primary raster Secondary raster (average values), unselected raster (variation in values)
SurfNetworkThread Primary raster (DEM) and secondary raster (surface features) Primary vector and secondary raster
SurfParamThread Primary raster Secondary raster
TinPointsThread Primary vector (points) Primary vector (TIN)
TinToDemThread Primary vector Primary raster
TransRastThread Primary raster Primary raster
UpdateThread None None (updates display)
VectorToRasterThread Empty raster and vector provided to the constructor Primary raster
View3dThread Primary raster, primary vector and active GraphicsArea None
VoidRemovalThread Primary raster Primary raster



4.2 Threaded Processing

All the processing classes mentioned above inherit LSThread which handles messages by the processor and allows the class to be threaded. This can be useful for constructing GUIs, as many of the processing tasks can take some time to complete. By threading their processing, other GUI activities can continue while processing takes place. To execute one of the processing classes as a threaded process, simply create an object from it and call its start() method. For example,

SurfParamThread processor = new SurfParamThread(gisFrame, mySurfParam);
processor.start();


This will call the processor's doProcessing() method automatically, and inform the parent GUI (gisFrame) when it is complete. Calling the start() method is preferred to calling doProcessing() directly, since the former also initialises the message communication between the processing class as the gisFrame.

If you are creating a command line interface where GUI activities are not going to be taking place while the process runs, it may make more sense not to run a process in parallel with other activities. In particular, it may be necessary to know when the process has completed before, for example, saving an output file to disk. The easiest way to do this is probably to let your class 'join' the thread (in other words, halt further execution until the thread has completed). For example,

SurfParamThread processor = new SurfParamThread(gisFrame, mySurfParam);
processor.start();
processor.join();
// This point is reached when the processor has completed its task.


Alternatively, you can poll the thread repeatedly until it has completed by calling the thread's isAlive() method.

An example illustrating these ideas showing how processing classes may be used is shown below:

import jwo.landserf.structure.*;    // For spatial object class.
import jwo.landserf.gui.*;          // For GISFrame.
import jwo.landserf.process.*;      // For processing classes.
import jwo.landserf.process.io.*;   // For file handling.

// **********************************************************
/** Creates a fractal surface and contours it. Demonstrates
  * how processing classes can be used. 
  * @author Jo Wood
  * @version 1.0, 12th November, 2004
  */
// **********************************************************

public class CreateFractal
{
    //------------------ Starter Method -----------------
    
    public static void main(String[] args)
    {
        new CreateFractal();
    }

    //------------------- Constructor -------------------

    public CreateFractal()
    {
        // Create a GISFrame that will store raster and vector maps.
        GISFrame gisFrame = new SimpleGISFrame();
        
        // Add a blank raster to the GISFrame.
        RasterMap fracRaster = new RasterMap(400,400,new Footprint(450000,280000,50,50));
        fracRaster.getHeader().setTitle("Fractal surface");
        gisFrame.addRaster(fracRaster,GISFrame.PRIMARY);
        
        // Create and start threaded process to generate fractal surface.
        FracSurfaceThread fracThread = new FracSurfaceThread(gisFrame,2.2f);
        fracThread.start();
        try
        {
            fracThread.join();    // Join thread (i.e. wait until it is complete).
        }
        catch (InterruptedException e)
        {
            System.err.println("Error: Fractal generation thread interrupted.");
            return;
        }
        System.out.println("Fractal surface created.");
        
        // Create and start a threaded process to contour the fractal.
        float minVal = fracRaster.getMinAttribute();
        float range = fracRaster.getMaxAttribute()-minVal;
        ContourThread contourThread = new ContourThread(gisFrame,minVal,range/30f,4);
        contourThread.start();
        try
        {
            contourThread.join();    // Join thread (i.e. wait until it is complete).
        }
        catch (InterruptedException e)
        {
            System.err.println("Error: Contouring thread interrupted.");
            return;
        }
        System.out.println("Fractal surface contoured.");
        
        // Write new raster and vector maps to file.
        LandSerfIO.write(fracRaster,"fractal.srf");
        LandSerfIO.write(gisFrame.getVectorMap1(),"contours.vec");
    }
}

Running the class above produces two new spatial objects - the raster map fractal.srf and the vector map contours.vec. They are shown below (since the fractal is randomly generated each time, the form of the surface will vary each time this program is run).

Fractal surface and contour output from CreateFractal Fractal surface and contour output from CreateFractal