Motivation: |
Improve GeoTools support for the SLD Raster Symbolizer. |
|
|---|---|---|
Contact: |
||
Tracker: |
||
Tagline: |
|
Implement RasterSymbolizer support for the GeoTools rendering system.
RasterSymbolizer defines a set of rules on how the client can instruct the renderer to create a portrayal of raster data. It introduces a set of operations that a rendering engine can perform consequently on raster data, like band selection, recolor, contrast enhancement and so on (see SE 1.1 and SLD 1.0 1.0 for reference).
The actual GeoTools SLD Raster Symbolizer implementation is rather poor and takes in account a very limited set of operations. Currently, the Raster Symbolizer functionalities are provided basically by the RasterSymbolizerSupport class represented in the diagram below.

The RasterSymbolizerSupport class has basically one method (recolorCoverage()) which extracts the color-map from the provided SLD's RasterSymbolizer element and tries to create a new set of Categories for the Coverage. Obviously, this approach only takes into account the ColorMap operation, also the result is not guaranted for all the cases, due to the fact that an input coverage may have no categories at all hence the recolorCoverage method may fail.
We describe here the approach we have followed to improve support for the RasterSymbolizer.
The work that has been performed can be summarized as follows:
The benefits are:
The diagrams below provides an overview of the proposed infrastructure for supporting piecewise transformation on raster values (click to enlarge diagram).
![]()
Here below the UML class diagram for the classes responsible for implementing SLD 1.0 rastersymbolizer (click to enlarge diagram).
![]()
Depending on the type of the CoveerageProcessingNode, it can be nested at different levels and invoked at different steps of the processing chain. The structure of the proposed API allows a developer to chain nodes as desired. The execute method produces an output GridCoverage2D used as input for the next node on the chain.
In order to improve the raster classification using categories, a new RasterClassifier JAI operation is proposed. This class inspects the raster classifing the raw values and range of values into categories, like for instance No-Data, but also prepares the image layout of the portrayal taking into account the original sample model of the raster, which usually cannot be directly represented as an image.
Note that javadocs for this work are attached.
As shown by the picture below particular care has been put on testing the provided capabilities. However, given the extensive refactor that this work has been undergoing lately to incorporate suggestions from the community we cannot guarantee to be 100% bug free (click to enlarge diagram).
![]()
From the user point of view, the Raster Symbolizer intervention support is fully transparent since it is hidden within the GridCoverageRender. Let's show some example how usage.
The following code provides an example of applying raster symbolizer on a sea bottom coverage through parsing of an SLD file.
// ////////////////////////////////////////////////////////////////////
//
// Test #1: \[SLD\]
// - Opacity: 1.0
// - ChannelSelection: Gray {Contrast Enh: Histogram}
//
// ////////////////////////////////////////////////////////////////////
java.net.URL surl = TestData.url(this, "histogram.sld");
SLDParser stylereader = new SLDParser(sf, surl);
StyledLayerDescriptor sld = stylereader.parseSLD();
// the RasterSymbolizer Helper
SubchainStyleVisitorCoverageProcessingAdapter rsh_SLD = new RasterSymbolizerHelper(gc, null);
// build the RasterSymbolizer
final UserLayer nl = (UserLayer) sld.getStyledLayers()\[0\];
final Style style = nl.getUserStyles()\[0\];
final FeatureTypeStyle fts = style.getFeatureTypeStyles()\[0\];
final Rule rule = fts.getRules()\[0\];
final RasterSymbolizer rs_1 = (RasterSymbolizer) rule.getSymbolizers()\[0\];
// visit the RasterSymbolizer
rsh_SLD.visit(rs_1).show();
|
StyleBuilder can also be used to manually build the Raster Symboliser element, see below.
// ////////////////////////////////////////////////////////////////////
//
// Test #1: \[StyleBuilder\]
// - Opacity: 1.0
// - ChannelSelection: Gray {Contrast Enh: Histogram}
//
// ////////////////////////////////////////////////////////////////////
// the RasterSymbolizer Helper
SubchainStyleVisitorCoverageProcessingAdapter rsh_StyleBuilder = new RasterSymbolizerHelper(gc, null);
// build the RasterSymbolizer
StyleBuilder sldBuilder = new StyleBuilder();
// the RasterSymbolizer Helper
rsh_StyleBuilder = new RasterSymbolizerHelper(gc, null);
final RasterSymbolizer rsb_1 = sldBuilder.createRasterSymbolizer();
final ChannelSelection chSel = new ChannelSelectionImpl();
final SelectedChannelType chTypeGray = new SelectedChannelTypeImpl();
final ContrastEnhancement cntEnh = new ContrastEnhancementImpl();
cntEnh.setHistogram();
chTypeGray.setChannelName("1");
chTypeGray.setContrastEnhancement(cntEnh);
chSel.setGrayChannel(chTypeGray);
rsb_1.setChannelSelection(chSel);
rsb_1.setOpacity(sldBuilder.literalExpression(1.0));
rsb_1.setOverlap(sldBuilder.literalExpression("AVERAGE"));
// visit the RasterSymbolizer
rsh_StyleBuilder.visit(rsb_1).show();
|
Here below the results of the code above with an input sonar image at which an Histogram Contrast Enhancement has been applied
Original Image |
Symbolized Image |
|---|---|
|
|
The SLD used:
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns="http://www.opengis.net/sld"
xmlns:ogc="http://www.opengis.net/ogc"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/sld
http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd?" version="1.0.0">
<UserLayer>
<Name>raster_layer</Name>
<LayerFeatureConstraints>
<FeatureTypeConstraint/>
</LayerFeatureConstraints>
<UserStyle>
<Name>raster</Name>
<Title>A boring default style</Title>
<Abstract>A sample style for rasters</Abstract>
<FeatureTypeStyle>
<FeatureTypeName>Feature</FeatureTypeName>
<Rule>
<RasterSymbolizer>
<!-- ColorMap type="ramp" extended="true">
<ColorMapEntry color="#000000" quantity="0.0" opacity="1.0"/>
<ColorMapEntry color="#ff0000" quantity="2.0" opacity="1.0"/>
<ColorMapEntry color="#ffff00" quantity="10.0" opacity="1.0"/>
<ColorMapEntry color="#00ff00" quantity="15.0" opacity="1.0"/>
<ColorMapEntry color="#00ffff" quantity="20.0" opacity="1.0"/>
<ColorMapEntry color="#0000ff" quantity="25.0" opacity="1.0"/>
<ColorMapEntry color="#ff00ff" quantity="30.0" opacity="1.0"/>
<ColorMapEntry color="#ffffff" quantity="100.0" opacity="1.0"/>
</ColorMap -->
<Opacity>1.0</Opacity>
<ChannelSelection>
<GrayChannel>
<SourceChannelName>1</SourceChannelName>
<ContrastEnhancement>
<Normalize/>
</ContrastEnhancement>
</GrayChannel>
</ChannelSelection>
<ContrastEnhancement>
<GammaValue>1</GammaValue>
</ContrastEnhancement>
</RasterSymbolizer>
</Rule>
</FeatureTypeStyle>
</UserStyle>
</UserLayer>
</StyledLayerDescriptor>
|
Original Image |
Gray Channel |
Symbolized Gray Channel |
|---|---|---|
|
|
|
A snippet of the original file:
NCOLS 278 NROWS 144 XLLCENTER 8.118000030517578 YLLCENTER 43.191001892089844 CELLSIZE 0.008999999478566561 NODATA_VALUE \-9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 ... ... -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 -9.0 0.36357998847961426 0.45179998874664307 0.5112000107765198 ... |
Portrayal using an SLD with no Color Map, Gray Channel selection and Histogram Contrast Enhancement:
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor xmlns=http://www.opengis.net/sld
xmlns:ogc="http://www.opengis.net/ogc"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" version="1.0.0">
<UserLayer>
<Name>raster_layer</Name>
<LayerFeatureConstraints>
<FeatureTypeConstraint/>
</LayerFeatureConstraints>
<UserStyle>
<Name>raster</Name>
<Title>A boring default style</Title>
<Abstract>A sample style for rasters</Abstract>
<FeatureTypeStyle>
<FeatureTypeName>Feature</FeatureTypeName>
<Rule>
<RasterSymbolizer>
<Opacity>1.0</Opacity>
<ChannelSelection>
<GrayChannel>
<SourceChannelName>1</SourceChannelName>
<ContrastEnhancement>
<Histogram/>
</ContrastEnhancement>
</GrayChannel>
</ChannelSelection>
</RasterSymbolizer>
</Rule>
</FeatureTypeStyle>
</UserStyle>
</UserLayer>
</StyledLayerDescriptor>
|
Sample output:

Portrayal using an& SLD with Color Map and Gray Channel selection
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor
xmlns="http://www.opengis.net/sld"
xmlns:ogc="http://www.opengis.net/ogc"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" version="1.0.0">
<UserLayer>
<Name>raster_layer</Name>
<LayerFeatureConstraints>
<FeatureTypeConstraint/>
</LayerFeatureConstraints>
<UserStyle>
<Name>raster</Name>
<Title>A boring default style</Title>
<Abstract>A sample style for rasters</Abstract>
<FeatureTypeStyle>
<FeatureTypeName>Feature</FeatureTypeName>
<Rule>
<RasterSymbolizer>
<ColorMap type="ramp" extended="true">
<ColorMapEntry color="#000000" quantity="-9.0" opacity="1.0"/>
<ColorMapEntry color="#cc0000" quantity="0.0" opacity="1.0"/>
<ColorMapEntry color="#ff0000" quantity="0." opacity="1.0"/>
<ColorMapEntry color="#ffcc00" quantity="0.2" opacity="1.0"/>
<ColorMapEntry color="#ccff00" quantity="0.3" opacity="1.0"/>
<ColorMapEntry color="#ccffcc" quantity="0.4" opacity="1.0"/>
<ColorMapEntry color="#00ffff" quantity="0.5" opacity="1.0"/>
<ColorMapEntry color="#00ffcc" quantity="0.6" opacity="1.0"/>
<ColorMapEntry color="#00ff00" quantity="0.7" opacity="1.0"/>
<ColorMapEntry color="#ccccff" quantity="0.8" opacity="1.0"/>
<ColorMapEntry color="#ccffff" quantity="0.9" opacity="1.0"/>
<ColorMapEntry color="#ffffff" quantity="1.0" opacity="1.0"/>
</ColorMap>
<Opacity>1.0</Opacity>
<ChannelSelection>
<GrayChannel>
<SourceChannelName>1</SourceChannelName>
</GrayChannel>
</ChannelSelection>
</RasterSymbolizer>
</Rule>
</FeatureTypeStyle>
</UserStyle>
</UserLayer>
</StyledLayerDescriptor>
|
Sample output:

The proposed approach should be backward compatible, since it does not require any kind of intrusive intervent to the GridCoverageRenderer since it completely replace the old RasterSymbolizerSupport class.The diagram depicted at the beginning of the document will change as shown below

Actually the Raster Symbolizer stuff presented here, is implemented on the GeoTools 2.4.x-RS branch. In order to complete the proposal the code should be ported to eoTools trunk.
Voting has not started yet:
|
no progress |
|
done |
|
impeded |
|
lack mandate/funds/time |
|
volunteer needed |
|---|