Raster Symbolizer support - STEMLab/geotools GitHub Wiki
-
Motivation: Improve GeoTools support for the SLD Raster Symbolizer.
-
Contact: Simone Giannecchini, Alessio Fabiani
-
Tagline: Refactoring the actual Raster Symbolizer architecture / Implementing the needed functionalities
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:
- check the SE 1.1 documentation for any changes relative to SLD 1.1 RasterSymbolizer
- development of piecewise1D transformation support classes for performing generic transformation on raster bands.
- development of coverage processing nodes implementing all the functionalities of the Raster Symbolizer as a processing chain.
The benefits are:
- the SLD Raster Symbolizer basic operations supported are:
- Channel Selection
- Color Map
- Contrast Enhancement
- algorithms actually supported:
- Histogram Normalize
- Histogram Equalize (ONLY ON BYTE DATA for the moment)
- Logarithmic Enhancement
- Exponential Enhancement
- algorithms actually supported:
- Opacity
- Gamma Correction
- this architecture allows to easily add more functionalities as needed, by simply implementing specific coverage processing nodes
- piecewise transformations allow full control for raster classification
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:
- Andrea Aime +0
- Ian Turton +1
- Justin Deoliveira +1
- Jody Garnett +1
- Martin Desruisseaux +0
- Simone Giannecchini +1
- Chris Holmes
| :white_check_mark: | :no_entry: | :warning: | :negative_squared_cross_mark: |
------------|--------------------|------------|-------------------------|-------------------------------| no progress | done | impeded | lack mandate/funds/time | volunteer needed |
- Do a quick check to make sure the classes mentioned in the proposal are non public
- ✅ Implement the Raster Symbolizer support
- ❎ Porting of the RS stuff on GeoTools 2.4.x
- Porting of the RS stuff on GeoTools trunk
- Copy the examples on this page over ot the user guide