/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Created on 13 November 2002, 13:47 */ package org.geotools.styling; import javax.measure.quantity.Length; import javax.measure.unit.Unit; import org.geotools.factory.CommonFactoryFinder; import org.geotools.factory.GeoTools; import org.opengis.filter.FilterFactory; import org.opengis.filter.expression.Expression; import org.opengis.style.OverlapBehavior; import org.opengis.style.StyleVisitor; /** * Default implementation of RasterSymbolizer. * * @author iant * @author Johann Sorel (Geomatys) * * @source $URL$ */ public class RasterSymbolizerImpl extends AbstractSymbolizer implements RasterSymbolizer { private OverlapBehavior behavior; // TODO: make container ready private FilterFactory filterFactory; private ChannelSelection channelSelection = new ChannelSelectionImpl(); private ColorMapImpl colorMap = new ColorMapImpl(); private ContrastEnhancementImpl contrastEnhancement = new ContrastEnhancementImpl(); private ShadedReliefImpl shadedRelief; private String geometryName = "geom"; private Symbolizer symbolizer; private Expression opacity; private Expression overlap; public RasterSymbolizerImpl(){ this( CommonFactoryFinder.getFilterFactory(GeoTools.getDefaultHints())); } public RasterSymbolizerImpl(FilterFactory factory) { this(factory,null,null,null,null); } public RasterSymbolizerImpl(FilterFactory factory, Description desc, String name, Unit<Length> uom, OverlapBehavior behavior) { super(name, desc, "grid", uom); this.filterFactory = factory; this.opacity = filterFactory.literal(1.0); this.overlap = filterFactory.literal(OverlapBehavior.RANDOM); this.behavior = behavior; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((behavior == null) ? 0 : behavior.hashCode()); result = prime * result + ((channelSelection == null) ? 0 : channelSelection.hashCode()); result = prime * result + ((colorMap == null) ? 0 : colorMap.hashCode()); result = prime * result + ((contrastEnhancement == null) ? 0 : contrastEnhancement.hashCode()); result = prime * result + ((filterFactory == null) ? 0 : filterFactory.hashCode()); result = prime * result + ((opacity == null) ? 0 : opacity.hashCode()); result = prime * result + ((overlap == null) ? 0 : overlap.hashCode()); result = prime * result + ((shadedRelief == null) ? 0 : shadedRelief.hashCode()); result = prime * result + ((symbolizer == null) ? 0 : symbolizer.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; RasterSymbolizerImpl other = (RasterSymbolizerImpl) obj; if (behavior == null) { if (other.behavior != null) return false; } else if (!behavior.equals(other.behavior)) return false; if (channelSelection == null) { if (other.channelSelection != null) return false; } else if (!channelSelection.equals(other.channelSelection)) return false; if (colorMap == null) { if (other.colorMap != null) return false; } else if (!colorMap.equals(other.colorMap)) return false; if (contrastEnhancement == null) { if (other.contrastEnhancement != null) return false; } else if (!contrastEnhancement.equals(other.contrastEnhancement)) return false; if (filterFactory == null) { if (other.filterFactory != null) return false; } else if (!filterFactory.equals(other.filterFactory)) return false; if (opacity == null) { if (other.opacity != null) return false; } else if (!opacity.equals(other.opacity)) return false; if (overlap == null) { if (other.overlap != null) return false; } else if (!overlap.equals(other.overlap)) return false; if (shadedRelief == null) { if (other.shadedRelief != null) return false; } else if (!shadedRelief.equals(other.shadedRelief)) return false; if (symbolizer == null) { if (other.symbolizer != null) return false; } else if (!symbolizer.equals(other.symbolizer)) return false; return true; } /** * The ChannelSelection element specifies the false-color channel selection * for a multi-spectral raster source (such as a multi-band * satellite-imagery source). Either a channel may be selected to display * in each of red, green, and blue, or a single channel may be selected to * display in grayscale. (The spelling ?gray? is used since it seems to * be more common on the Web than ?grey? by a ratio of about 3:1.) * Contrast enhancement may be applied to each channel in isolation. * Channels are identified by a system and data-dependent character * identifier. Commonly, channels will be labelled as ?1?, ?2?, etc. * * @return the ChannelSelection object set or null if none is available. */ public ChannelSelection getChannelSelection() { return channelSelection; } /** * The ColorMap element defines either the colors of a palette-type raster * source or the mapping of fixed-numeric pixel values to colors. For * example, a DEM raster giving elevations in meters above sea level can * be translated to a colored image with a ColorMap. The quantity * attributes of a color-map are used for translating between numeric * matrixes and color rasters and the ColorMap entries should be in order * of increasing numeric quantity so that intermediate numeric values can * be matched to a color (or be interpolated between two colors). Labels * may be used for legends or may be used in the future to match character * values. Not all systems can support opacity in colormaps. The default * opacity is 1.0 (fully opaque). Defaults for quantity and label are * system-dependent. * * @return the ColorMap for the raster */ public ColorMapImpl getColorMap() { return colorMap; } /** * The ContrastEnhancement element defines contrast enhancement for a * channel of a false-color image or for a color image. In the case of a * color image, the relative grayscale brightness of a pixel color is * used. ?Normalize? means to stretch the contrast so that the dimmest * color is stretched to black and the brightest color is stretched to * white, with all colors in between stretched out linearly. ?Histogram? * means to stretch the contrast based on a histogram of how many colors * are at each brightness level on input, with the goal of producing equal * number of pixels in the image at each brightness level on output. This * has the effect of revealing many subtle ground features. A ?GammaValue? * tells how much to brighten (value greater than 1.0) or dim (value less * than 1.0) an image. The default GammaValue is 1.0 (no change). If none * of Normalize, Histogram, or GammaValue are selected in a * ContrastEnhancement, then no enhancement is performed. * * @return the ContrastEnhancement */ public ContrastEnhancementImpl getContrastEnhancement() { return contrastEnhancement; } /** * The ImageOutline element specifies that individual source rasters in a * multi-raster set (such as a set of satellite-image scenes) should be * outlined with either a LineStringSymbol or PolygonSymbol. It is defined * as: * <pre> * <xs:element name="ImageOutline"> * <xs:complexType> * <xs:choice> * <xs:element ref="sld:LineSymbolizer"/> * <xs:element ref="sld:PolygonSymbolizer"/> * </xs:choice> * </xs:complexType> * </xs:element> * </pre> * An Opacity of 0.0 can be selected for the main raster to avoid rendering * the main-raster pixels, or an opacity can be used for a * PolygonSymbolizer Fill to allow the main-raster data be visible through * the fill. * * @return The relevent symbolizer */ public Symbolizer getImageOutline() { return symbolizer; } /** * fetch the expresion which evaluates to the opacity fo rthis coverage * * @return The expression */ public Expression getOpacity() { return opacity; } /** * The OverlapBehavior element tells a system how to behave when multiple * raster images in a layer overlap each other, for example with * satellite-image scenes. LATEST_ON_TOP and EARLIEST_ON_TOP refer to the * time the scene was captured. AVERAGE means to average multiple scenes * together. This can produce blurry results if the source images are not * perfectly aligned in their geo-referencing. RANDOM means to select an * image (or piece thereof) randomly and place it on top. This can * produce crisper results than AVERAGE potentially more efficiently than * LATEST_ON_TOP or EARLIEST_ON_TOP. The default behaviour is * system-dependent. * * @return The expression which evaluates to LATEST_ON_TOP, * EARLIEST_ON_TOP, AVERAGE or RANDOM */ @Deprecated public Expression getOverlap() { return overlap; } public OverlapBehavior getOverlapBehavior() { return behavior; } public void setOverlapBehavior(OverlapBehavior overlapBehavior) { this.behavior = overlapBehavior; } /** * The ShadedRelief element selects the application of relief shading (or * ?hill shading?) to an image for a three-dimensional visual effect. It * is defined as: Exact parameters of the shading are system-dependent * (for now). If the BrightnessOnly flag is ?0? (false, default), the * shading is applied to the layer being rendered as the current * RasterSymbol. If BrightnessOnly is ?1? (true), the shading is applied * to the brightness of the colors in the rendering canvas generated so * far by other layers, with the effect of relief-shading these other * layers. The default for BrightnessOnly is ?0? (false). The * ReliefFactor gives the amount of exaggeration to use for the height of * the ?hills.? A value of around 55 (times) gives reasonable results for * Earth-based DEMs. The default value is system-dependent. * * @return the shadedrelief object */ public ShadedReliefImpl getShadedRelief() { return shadedRelief; } /** * The ChannelSelection element specifies the false-color channel selection * for a multi-spectral raster source (such as a multi-band * satellite-imagery source). Either a channel may be selected to display * in each of red, green, and blue, or a single channel may be selected to * display in grayscale. (The spelling ?gray? is used since it seems to * be more common on the Web than ?grey? by a ratio of about 3:1.) * Contrast enhancement may be applied to each channel in isolation. * Channels are identified by a system and data-dependent character * identifier. Commonly, channels will be labelled as ?1?, ?2?, etc. * * @param channel the channel selected */ public void setChannelSelection(org.opengis.style.ChannelSelection channel) { if (this.channelSelection == channel) { return; } this.channelSelection = ChannelSelectionImpl.cast( channel ); } /** * The ColorMap element defines either the colors of a palette-type raster * source or the mapping of fixed-numeric pixel values to colors. For * example, a DEM raster giving elevations in meters above sea level can * be translated to a colored image with a ColorMap. The quantity * attributes of a color-map are used for translating between numeric * matrixes and color rasters and the ColorMap entries should be in order * of increasing numeric quantity so that intermediate numeric values can * be matched to a color (or be interpolated between two colors). Labels * may be used for legends or may be used in the future to match character * values. Not all systems can support opacity in colormaps. The default * opacity is 1.0 (fully opaque). Defaults for quantity and label are * system-dependent. * * @param colorMap the ColorMap for the raster */ public void setColorMap(org.opengis.style.ColorMap colorMap) { if (this.colorMap == colorMap) { return; } this.colorMap = ColorMapImpl.cast( colorMap ); } /** * The ContrastEnhancement element defines contrast enhancement for a * channel of a false-color image or for a color image. In the case of a * color image, the relative grayscale brightness of a pixel color is * used. ?Normalize? means to stretch the contrast so that the dimmest * color is stretched to black and the brightest color is stretched to * white, with all colors in between stretched out linearly. ?Histogram? * means to stretch the contrast based on a histogram of how many colors * are at each brightness level on input, with the goal of producing equal * number of pixels in the image at each brightness level on output. This * has the effect of revealing many subtle ground features. A ?GammaValue? * tells how much to brighten (value greater than 1.0) or dim (value less * than 1.0) an image. The default GammaValue is 1.0 (no change). If none * of Normalize, Histogram, or GammaValue are selected in a * ContrastEnhancement, then no enhancement is performed. * * @param contrastEnhancement the contrastEnhancement */ public void setContrastEnhancement(org.opengis.style.ContrastEnhancement contrastEnhancement) { if (this.contrastEnhancement == contrastEnhancement) { return; } this.contrastEnhancement = ContrastEnhancementImpl.cast( contrastEnhancement ); } /** * The ImageOutline element specifies that individual source rasters in a * multi-raster set (such as a set of satellite-image scenes) should be * outlined with either a LineStringSymbol or PolygonSymbol. It is defined * as: * <pre> * <xs:element name="ImageOutline"> * <xs:complexType> * <xs:choice> * <xs:element ref="sld:LineSymbolizer"/> * <xs:element ref="sld:PolygonSymbolizer"/> * </xs:choice> * </xs:complexType> * </xs:element> * </pre> * An Opacity of 0.0 can be selected for the main raster to avoid rendering * the main-raster pixels, or an opacity can be used for a * PolygonSymbolizer Fill to allow the main-raster data be visible through * the fill. * * @param symbolizer the symbolizer to be used. If this is <B>not</B> a * polygon or a line symbolizer an unexpected argument exception * may be thrown by an implementing class. * * @throws IllegalArgumentException DOCUMENT ME! */ public void setImageOutline(org.opengis.style.Symbolizer symbolizer) { if( symbolizer == null ){ this.symbolizer = null; } else if (symbolizer instanceof LineSymbolizer || symbolizer instanceof PolygonSymbolizer) { if (this.symbolizer == symbolizer) { return; } this.symbolizer = StyleFactoryImpl2.cast( symbolizer ); } else { throw new IllegalArgumentException( "Only a line or polygon symbolizer may be used to outline a raster"); } } /** * sets the opacity for the coverage, it has the usual meaning. * * @param opacity An expression which evaluates to the the opacity (0-1) */ public void setOpacity(Expression opacity) { if (this.opacity == opacity) { return; } this.opacity = opacity; } /** * The OverlapBehavior element tells a system how to behave when multiple * raster images in a layer overlap each other, for example with * satellite-image scenes. LATEST_ON_TOP and EARLIEST_ON_TOP refer to the * time the scene was captured. AVERAGE means to average multiple scenes * together. This can produce blurry results if the source images are not * perfectly aligned in their geo-referencing. RANDOM means to select an * image (or piece thereof) randomly and place it on top. This can * produce crisper results than AVERAGE potentially more efficiently than * LATEST_ON_TOP or EARLIEST_ON_TOP. The default behaviour is * system-dependent. * * @param overlap the expression which evaluates to LATEST_ON_TOP, * EARLIEST_ON_TOP, AVERAGE or RANDOM */ @Deprecated public void setOverlap(Expression overlap) { if (this.overlap == overlap) { return; } this.overlap = overlap; } /** * The ShadedRelief element selects the application of relief shading (or * ?hill shading?) to an image for a three-dimensional visual effect. It * is defined as: Exact parameters of the shading are system-dependent * (for now). If the BrightnessOnly flag is ?0? (false, default), the * shading is applied to the layer being rendered as the current * RasterSymbol. If BrightnessOnly is ?1? (true), the shading is applied * to the brightness of the colors in the rendering canvas generated so * far by other layers, with the effect of relief-shading these other * layers. The default for BrightnessOnly is ?0? (false). The * ReliefFactor gives the amount of exaggeration to use for the height of * the ?hills.? A value of around 55 (times) gives reasonable results for * Earth-based DEMs. The default value is system-dependent. * * @param shadedRelief the shadedrelief object */ public void setShadedRelief(org.opengis.style.ShadedRelief shadedRelief) { if (this.shadedRelief == shadedRelief) { return; } this.shadedRelief = ShadedReliefImpl.cast( shadedRelief ); } public Object accept(StyleVisitor visitor,Object data) { return visitor.visit(this,data); } public void accept(org.geotools.styling.StyleVisitor visitor) { visitor.visit(this); } /** * Creates a deep copy clone. TODO: Need to complete the deep copy, * currently only shallow copy. * * @return The deep copy clone. * * @throws RuntimeException DOCUMENT ME! */ public Object clone() { Object clone; try { clone = super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); // this should never happen. } return clone; } static RasterSymbolizerImpl cast(org.opengis.style.Symbolizer symbolizer) { if( symbolizer == null ){ return null; } if( symbolizer instanceof RasterSymbolizerImpl ){ return (RasterSymbolizerImpl) symbolizer; } else if (symbolizer instanceof org.opengis.style.RasterSymbolizer ){ org.opengis.style.RasterSymbolizer rasterSymbolizer = (org.opengis.style.RasterSymbolizer) symbolizer; RasterSymbolizerImpl copy = new RasterSymbolizerImpl(); copy.setChannelSelection( rasterSymbolizer.getChannelSelection()); copy.setColorMap( rasterSymbolizer.getColorMap() ); copy.setContrastEnhancement( rasterSymbolizer.getContrastEnhancement() ); copy.setDescription( rasterSymbolizer.getDescription()); copy.setGeometryPropertyName( rasterSymbolizer.getGeometryPropertyName() ); copy.setImageOutline( rasterSymbolizer.getImageOutline() ); copy.setName( rasterSymbolizer.getName()); copy.setOpacity( rasterSymbolizer.getOpacity()); copy.setOverlapBehavior( rasterSymbolizer.getOverlapBehavior() ); copy.setShadedRelief( rasterSymbolizer.getShadedRelief()); copy.setUnitOfMeasure( rasterSymbolizer.getUnitOfMeasure()); return copy; } return null; // must not be a raster symbolizer } }