ContourGrid
Class to make an ArrayList of ContourFeature-s from a regular 2D data grid.
Creates contours, and adds all the contours
to an ArrayList of ContourFeatures, given some input data grid.
Contour levels desired are an input argument list.
Constructor input:
ucar.ma2.Array dataGrid:
the 2D grid of values at grid points to contour;
double []-s xPosition, yPosition: the list of coordinates of grid points
along x and y axes;
ArrayList allContourValues a list of the desired contour level values;
GeoGridImpl geogrid
How it works:
Contouring is activated by calling the function getContourLines().
The contours for the dataGrid input to the cstr are made.
internal variables
direction is indicated by the char values N, S, E, and W,
indicating directions in the grid
contourValues are the contour levels used to make contours; derived
from the input contour values to the constructor. If the input
array contourValues is of zero length, contour values are automatically
generated.
xMaxInd and yMaxInd are the largest array indices
in x and y permitted for this
grid. One less than dimension.
contourOnVertlEdge is an int array the size of the data grid,
with each value 0 or 1. This is used
for an indication whether a contour of a certain level crosses between this
point and the next one of one larger y index.
This array is recalculated for each contour level.
contourOnHorizEdge is similar indication whether a contour of a
certain level crosses between this
point and the next one of one larger x index.
These arrays are recalculated for each contour level.
conLevel is the active contour level during computations.
The algorithm works as follows.
Each grid cell is identified by the lower left grid point (i,j).
The cell edge to the right of (i,j) is the horizontal edge for grid cell
(i,j) and the cell edge above (i,j) is the vertical edge.
i increases to the right and j increases upward.
Contouring proceeds one level at a time, starting at the lowest level.
For each level the crossing indicators contourOnVertlEdge and
contourOnHorizEdge are set first.
When a contour level value lies between the grid value at (i,j) and value
at (i+1,j) then contourOnHorizEdge(i,j) is set to 1; otherwise 0.
When a contour level value
lies between the grid value at (i,j) and the value at (i,j+1) then
contourOnVertlEdge(i,j) is set to 1; otherwise 0. This is done
by the function setupForContoursAt(double level). Note that only one
contour crossing for one level is allowed
between two grid points. This is consistent with the sampling of the actual
data field by the grid points,
though it is possible that higher data resolution could sometimes
show two or more contours
at a level passing between two of the grid points.
Then the cell edges along the west edge of the grid are checked to see
if any contour crossings occur
(function searchWestEdge()).
If a contour is found crossing the edge associated with the cell (i,j) the
function followContour(Dir, i, j) is called, where Dir is one of the
directions indicating a contour was found starting on the west edge.
followContour follows this contour through the grid until it reaches its end
against a grid edge (it
can't close on itself since it started on an edge and only one crossing is
permitted at a cell edge).
As it works through the contour, contour positions are appended to the
current line.
This contouring code works in the (i,j) main GRID INDEX system, and
contour positions have double (non-integer) values
in the main grid "index" units. But the code ends by computing
all contour positions in the x and y coord system input to the cstr.
So as followContour works along the contour it finds the points where the
contour crossed each cell
edge, using the indicators contourOnVertlEdge and contourOnHorizEdge, and
the function
contourEdgeIntersection(side, int i, int j). The function
contourEdgeIntersection() returns the position where the contour crosses
the edge in main grid coordinates. The conversion is
made to desired coordinate units,
and that position is appended to the current contourLine.
Having a position where a contour enters a main grid cell, the heading or
side where the contour
leaves the cell is returned by function directionToGoFrom(side, i, j).
Intermediate points inside the
main cell are found by contourEdgeIntersection()
which also returns the next main grid point
on the cell edge. This process is continued until
the contour reaches its end against an edge.
The functions followContour(...),
contourEdgeIntersection(...)& directionToGoFrom(...)
are the core of contouring.
Having found all contours starting on the west edge of the main grid, the
south and east edges are
checked in the same way with functions searchSouthEdge() and
searchEastEdge(). Then the north
edges of all cells on the north edge, and all interior points, are checked
to find any contours not
yet found, including internal contours which close on themselves.
All this was for one contour level. The process is repeated for
every contour level.
Some tricks to contouring appear when details of how contours can cross a
cell are considered. Normally a single contour for one level
comes in one side of a cell and goes out a different side. It
is possible for a data grid point to exactly equal the contour level, in
which case the side associated
with that level is not determined. In this case the data value is shifted by
adding ((gridmax - gridmin) /100000.0)
This is simpler than changing the code to deal with this case,
and has no observable effect on earth science data
displays which is presumed to always have a much larger data range than
((gridmax - gridmin) /100000.0).
Two contours of the same level may cross one cell, each crossing two
differing edges. This is the
case of a saddle point. Crossing point pairs are selected by choosing the
nearest crossing point to
the incoming point, in function directionToGoFrom.
It does not appear possible to have three edges be crossed or touched by a
single contour. If you think otherwise, please try to find four data
values for cell corners which fit the case.