Styling features for display ---------------------------- GeoTools Rendering process is controlled styling information that you provide. The data structures we use to describe styling are based on the Style Layer Descriptor (SLD) and Symbology Encoding (SE) specifications provided by the OGC. The SLD specification defines an XML document you can use to save and load your styles. This page is devoted to examples, to review the concepts consult the references below. References: * :doc:`gt-api style layer descriptor <../api/sld>` (interfaces) * :doc:`gt-api symbology encoding <../api/se>` (interfaces) * http://www.opengeospatial.org/standards/sld (style layer descriptor) * http://www.opengeospatial.org/standards/symbol (symbology encoding) StyleFactory ^^^^^^^^^^^^ We have two approaches offering various levels of standards compliance vs pragmatism. ============== ==================== ================ ======== ============================================ Module Class Capability Scope Description ============== ==================== ================ ======== ============================================ ``gt-api`` ``StyleFactory`` get/set SE / SLD SE, SLD, and vendor extensions ``gt-main`` ``StyleBuilder`` get/set/defaults SE / SLD Shorter methods, does not do everything ``gt-brewer`` ``StyleBuilder`` fluent SE / SLD Fluent API for SE, SLD and vendor extensions ============== ==================== ================ ======== ============================================ Here are some examples of these classes in action: * ``StyleFactory`` StyleFactory allows creation using the parameters defined by the SLD standard. Here is a quick example showing the creation of a ``PointSymbolizer``: .. literalinclude:: /../src/main/java/org/geotools/api/StyleExamples.java :language: java :start-after: // styleFactoryExample start :end-before: // styleFactoryExample end To work with GeoTools vendor specific options, a slightly different style of programming is needed taking advantage of the mutable instances are creating allowing you to call both get and set methods. .. note:: These classes are not thread-safe, do not update a style while it is being used to draw. .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // styleFactoryExample start :end-before: // styleFactoryExample end * ``StyleBuilder`` from ``gt-main``: Since a ``Style`` is composed of a complex set of objects, a ``StyleBuilder`` object is provided to build simple styles without the need to build all of the style elements by hand. For example, you can create a ``PolygonSymbolizer`` and then create a ``Style`` out of it with a single method call: the builder will generate a default ``FeatureTypeStyle`` and the ``Rule`` for you. .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // styleBuilderExample start :end-before: // styleBuilderExample end ``StyleBuilder`` also helps by filling in many defaults values. The use of defaults is less of an issue now as the rendering system is able to correctly handle null as a default for many cases such as default symbol size. * ``StyleBuilder`` from ``gt-brewer``: .. code-block:: java Style style = new StrokeBuilder().color(Color.BLACK).width(3).buildStyle(); **What to use** For working with symbology encoding ``StyleFactory`` is recommended as it defines a small number of easy to use methods. There are however no helpful methods and shortcuts (but you have the advantage of less methods to trip over). Since everything is in plain sight you may discover some tricky advanced abilities that may not obvious using ``StyleBuilder``. For working with style layer descriptor use ``StyleBuilder`` to quickly create objects with their default values filled in; and then configure them as needed using setters. Internally we have: * ``StyleFactoryImpl2`` that creates the raw objects * ``StyleFactoryImpl`` makes use of a delegate to create the objects; and then allows for a wider range of create methods defined by ``gt-api`` ``StyleFactory`` * ``StyleBuilder`` uses a ``FilterFactory`` and a ``StyleFactory`` in order build up a complicated data structure Style Layer Descriptor ^^^^^^^^^^^^^^^^^^^^^^ GeoTools styling is built on the style layer descriptor data model shown below (from :doc:`gt-api <../api/sld>`). .. image:: /images/sld.PNG GeoTools rendering tends to focus the "User Style" which we represent ``Style`` to let you control how your Map is rendered. * ``Style`` The ``Style`` interface matches up with the "Style Layer Descriptor" 1.0 specification (so if you need explanations or examples please review the OGC documentation for more information). Create '''''' To create a ``StyleLayerDescriptor`` object using a ``StyleFactory``: .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // sldExample start :end-before: // sldExample end This is the last time we will talk about ``StyleLayerDescriptor`` object - it is not really that useful in controlling the rendering process. Access '''''' To go from a ``StyleLayerDescriptor`` object to something useful:: FeatureTypeStyle useful[] = SLD.featureTypeStyles( sld ); Or find a style that is compatible with your feature type:: FeatureTypeStyle applicable = SLD.featureTypeStyle( sld, schema ); Document '''''''' The Styled Layer Descriptor Reference Document OpenGIS standard defines an XML document we use to persist our GeoTools Style objects. This standard is the authoritative definition as far as functionally goes, if you find any place where we are out of line please send us a bug report. How to parse an SLD file ------------------------- * You can create a ``Style`` using an SLD document (an XML file format defined by the Style Layer Descriptor 1.0 specification): .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // parseSLD start :end-before: // parseSLD end * SAX ``StyleReader`` A SAX based ``StyleReader`` is also available for GeoTools 2.2 code:: private Style loadStyleFromXml() throws Exception { java.net.URL base = getClass().getResource("rs-testData"); StyleFactory factory = StyleFactory.createStyleFactory(); java.net.URL surl = new java.net.URL(base + "/markTest.sld"); //A class to read and parse an SLD file based on verion 0.7.2 of the OGC SLDStyle stylereader = new SLDStyle(factory, surl); Style[] style = stylereader.readXML(); return style[0]; } How to write a SLD file ------------------------ * GeoTools has an XML Transfer written up allowing you to generate an SLD file:: SLDTransformer styleTransform = new SLDTransformer(); String xml = styleTransform.transform(sld); * How to write an SLD file using only a Style The above code example requires a complete ``StyleLayerDescriptor`` document in order to make a valid SLD file. Here is how you can wrap up your ``Style`` object for output:: StyledLayerDescriptor sld = styleFactory.createStyledLayerDescriptor(); UserLayer layer = styleFactory.createUserLayer(); layer.setLayerFeatureConstraints(new FeatureTypeConstraint[] {null}); sld.addStyledLayer(layer); layer.addUserStyle(style); SLDTransformer styleTransform = new SLDTransformer(); String xml = styleTransform.transform(sld); XML: * The file :download:`markTest.sld ` contains the following XML: .. literalinclude:: /../../modules/library/xml/src/test/resources/org/geotools/xml/styling/test-data/markTest.sld * The same style can be created using the ``StyleBuilder``: .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // markTestSLD start :end-before: // markTestSLD end As an extension GeoTools supports defining a Style ``Background`` object that will be used to fill the canvas before the style rendering directives are applied to features and coverages. Here is how to setup a style with background: .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // styleBackground start :end-before: // styleBackground end and a similar setup :download:`as a SLD `: .. literalinclude:: /../../modules/library/xml/src/test/resources/org/geotools/xml/styling/test-data/backgroundSolid.sld Symbology Encoding ^^^^^^^^^^^^^^^^^^ The feature type style data model captures the symbology encoding information describing how a feature should be drawn on the screen and will represent the bulk of our examples. .. image:: /images/se.PNG FeatureTypeStyle '''''''''''''''' A ``FeatureTypeStyle`` declares a part of a style that is specifically geared toward a ``FeatureType``, that is, features will be rendered according to this ``FeatureTypeStyle`` only if their ``FeatureType`` is the same as the ``FeatureType`` declared in the ``FeatureTypeStyle`` or a descendant. When defining a Style you will spend the majority of time working with ``FeatureTypeStyle``. A ``FeatureTypeStyle`` is specifically geared toward drawing features. The level of detail is similar to CSS in that you need to define some Rules saying when to draw, and some symbolizers saying how to draw it. Individual symbolizers will use expressions to access feature content (as an example ``TextSymbolizer`` will use an expression you provide to construct the text to display). * ``FeatureTypeStyle.getName()``\ : machine readable name * ``FeatureTypeStyle.getDescriptor()``\ : human readable title and description * ``FeatureTypeStyle.featureTypeNames()``\ : the Name here is important; it must match the Features you want to draw. ``Features`` will be rendered according a ``FeatureTypeStyle`` only if their ``FeatureType`` name matches what is recorded in the ``FeatureTypeStyle`` or a descendant. For most practical purposes you will set ``featureTypeName`` to be "Feature" to act as a wild card. * ``FeatureTypeStyle.semanticTypeIdentifiers()``\ : used to quickly limit based on the kind of vector data (point, line or polygon) Here is a quick example that will draw any "Feature" using a ``PointSymbolizer``: .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // featureTypeStyleExample start :end-before: // featureTypeStyleExample end .. note:: how the ``PointSymbolizer`` will first try and use ``C:\images\house.gif`` (i.e. an external graphic) and if that fails it will use a circle (i.e. a mark). Notes on handling of features: * Each ``FeatureTypeStyle`` that applies is used. That means, the layer will be drawn multiple times if the styles contain more than one ``FeatureTypeStyle`` that matches the ``FeatureType`` of the features in the layer. * ``FeatureTypeStyles`` are painted in order: a ``FeatureTypeStyle`` is painted only once the previous one in the list has been completely painted * Each feature is then passed to the rules and their list of symbolizers * This means that a single feature can be painted more than once, if more than one rule matches it, or if the rules contain more than one symbolizer. * The full set of rules and symbolizers in the current ``FeatureTypeStyle`` is applied to the current Feature before considering the next one. The last consideration is important when you need to draw, for example, roads with a double line such as a wide black line below a thin white line. This is possible using two ``FeatureTypeStyles``, since using a Rule with a couple of symbolizers will generate a map that doesn't look good at road intersections. Example of style with two ``FeatureTypeStyles``: .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // twoFeatureTypeStyles start :end-before: // twoFeatureTypeStyles end * For an in depth discussion of the rendering process please refer to * :doc:`style ` (tutorial) ``FeatureTypeStyle`` includes vendor options specific to the GeoTools rendering engine: * ``composite`` (source-over): allows control of color blending (using copy destination, source-over, destination-over, source-in, destination-in, source-out, destination-out source-atop, destination-atop, xor, multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion). * ``composite``-base (false): definition of composition groups .. code-block:: java // multiply buffer from feature type style 0, onto composite-base provided by feature type style 1 style.featureTypeStyles().get(0).getOptions().put("composite","multiply, 0.5"); style.featureTypeStyles().get(1).getOptions().put("composite-base", "true"); * ``firstMatch``: stops rule evaluation after the first match (making it easier to work with data sets where content is classified by distinct attribute values) .. code-block:: java // exit rules on first match, like a switch statement fts.getOptions().put("ruleEvaluation", "first"); * ``sortBy``: Control order features are retrieved, controlling drawing order. The syntax is `Attribute1 {A|D},Attribute2 {A|D}...` where `A` is ascending, `D` is descending. The sorting direction is optional and defaults to ascending if not specified. .. code-block:: java // sort newer cities first, than by name fts.getOptions().put( "sortBy", "year D,name A"); Rule '''' A ``FeatureTypeStyle`` contains one or more rules, these rules are considered in order with the possibility of an "else" Rule being used to render any remaining features. A rule is based on the following: * minimum/maximum scale: if set and the current scale is outside the specified range, the rule won't apply and thus its symbolizers won't be used * Filter: that is applied to the features, only the features matching the filter will be painted according to the Rule symbolizers. As an alternative, the rule can have an "else filter". This special kind of filter catches all of the features that still haven't been symbolized by previous rules with a regular filter. ``FeatureTypeStyle`` used ``featureTypename`` to sort out what kind of features we are dealing with. ``Rules`` are used to refine this contents, possibly filtering according to feature attributes or scale, to determine specifically what we are going to draw. Pay Attention to: * minimum and maximum map scale, if set and the current scale is outside the specified range, the rule won't apply and thus its symbolizers won't be used * Filter that is used to select features to draw, only the features matching the filter will be painted * A rule can have an "else filter". This special kind of filter catches all of the features that still haven't been symbolized by previous rules with a regular filter). Once ``FeatureTypeStyle`` and ``Rules`` have determined that a ``Feature`` is going to be drawn; the ``Rule`` makes use of a list of of ``Symbolizers`` to define how the content is painted: * A ``Symbolizer`` describes how to represent a feature on the screen based on the feature contents (geometry and attributes). * Each ``Rule`` can have a list of ``Symbolizer`` attached to it. * ``Symbolizer``\ s are used like a display language to produce pixels on the display device. Symbolizer '''''''''' A ``Symbolizer`` describes how a ``Feature`` should appear on a map. Each ``Rule`` has a list of ``Symbolizer``\ s which it applies in order. .. image:: /images/symbolizer3.PNG As you can see, there are many kind of symbolizers, for points, lines, polygons, labels and raster data. You don't need to match the symbolizer with the specific geometry contained in the feature, the renderer will try to do the most appropriate thing on a case by case basis. For example, ``TextSymbolizer`` applies to all kinds of geometries, and will generate labels on the map. If you apply a ``PolygonSymbolizer`` to a line, the line will be closed to form a polygon, and then the polygon symbolizer will be applied. .. image:: /images/symbolizer2.PNG The GeoTools ``Symbolizer`` interface offers a couple of advantages over the base standard: * ``getGeometry()`` * ``setGeometry( Expression )`` The ability to define a geometry using an expression allows the use of a function to pre-process your geometry prior to it being considered for rendering. This is a little bit tricky (as functions like buffer will make your geometry bigger) but the result is worthwhile in the amount of flexibility it offers. Notes on the use of symbolizers: * The symbolizer describes not just the shape that should appear but also such graphical properties as color and opacity * Symbolizers do have a default behavior, after creating a Symbolizer you should supplying parameters to override the default settings * The original details of this object are taken from the OGC Styled-Layer Descriptor Report (OGC 01-077) version 0.7.2. * Renderers can use this information when displaying styled features. Though it must be remembered that not all renderers will be able to fully represent strokes as set out by this interface. For example, opacity may not be supported. * The graphical parameters and their values are derived from SVG/CSS2 standards with names and semantics which are as close as possible. * The most important thing to note here is that symbolizer component objects are composed of Expression objects, which means that they may be made dependent on Feature attributes. For example, you can create a mathematical expression that links some Feature attribute to the line width. Thus, you have two ways to symbolize different features with different styles: * By using more than one rule with different filters, and then building symbolizers with literal expressions. This is a good way to create a classified map, in which colors, line styles and so on depend on the range the attribute value falls into. * By directly linking a symbolizer property to an attribute value; Vendor options suitable for use with any symbolizer: * composite (source-over): allows control of color blending (using copy destination, source-over, destination-over, source-in, destination-in, source-out, destination-out source-atop, destination-atop, xor, multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion). Point Symbolizer '''''''''''''''' Used to draw a point location, the actual graphic drawn is referred to as a Mark with the option to use some well known marks (circle, square etc..) or your own external graphics such as PNG icons. Examples: * `GeoServer SLD cookbook points ` * Quick example creating a ``PointSymbolizer`` using ``StyleBuilder``: .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // quickPointSymbolizer start :end-before: // quickPointSymbolizer end Here is the same style as an xml fragments:: size rotation name #FF0000 0.5 Point symbolizers support ``VendorOptions``: * ``labelObstacle(true/false)``\ : No labels should overlap this feature, used to ensure point graphics are clearly visible and not obscured by text. * ``fallbackOnDefaultMark(true/false)``\ : If the graphics used in the symbolizer cannot be found, fallback on a default gray square (``true``, default value) or skip the symbolizer without painting anything (``false``). LineSymbolizer '''''''''''''' Used to control how lines (or edges) are drawn. Examples: * `GeoServer SLD cookbook Lines ` PolygonSymbolizer ''''''''''''''''' Used to control how solid shapes are drawn. Examples: * `GeoServer SLD cookbook Polygons ` * Quick example using ``StyleBuilder`` to create a ``PolygonSymbolizer``: .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // quickPolygonSymbolizer start :end-before: // quickPolygonSymbolizer end TextSymbolizer '''''''''''''' Used to control the labeling system; labels are generated by ``TextSymbolizers`` and thrown into the rendering engine which detect overlaps, sorts things out according to priorities you have defined and decides on a final label placement. This makes ``TextSymbolizer`` a little bit odd in that it does not always get the final say on how labels are rendered on a pixel by pixel basis. Examples: * GeoServer SLD cookbook * :geoserver:`Point Labels ` * :geoserver:`Line Labels ` * :geoserver:`Polygon Labels ` * Here is a quick example of creating a ``TextSymbolizer`` with ``StyleBuilder``: .. literalinclude:: /../src/main/java/org/geotools/render/StyleExamples.java :language: java :start-after: // quickTextSymbolizer start :end-before: // quickTextSymbolizer end Here is the same example as an xml fragment:: Lucida Sans 10 X Y #000000 4 circle #FF0000 Considerable vendor options are provided for working with ``TextSymbolizers``: * ``allowOverruns(false)``\ : When false does not allow labels on lines to get beyond the beginning/end of the line. By default a partial overrun is tolerated, set to false to disallow it. * ``autoWrap(400)``\ : Number of pixels are which a long label should be split into multiple lines. Works on all geometries, on lines it is mutually exclusive with the ``followLine`` option * ``conflictResolution(true)``\ : Enables conflict resolution (default, true) meaning no two labels will be allowed to overlap. Symbolizers with conflict resolution off are considered outside of the conflict resolution game, they don't reserve area and can overlap with other labels. * ``followLine(true)``\ : When true activates curved labels on linear geometries. The label will follow the shape of the current line, as opposed to being drawn a tangent straight line * ``forceLeftToRight(true)``\ : When true forces labels to a readable orientation, when false they make follow the line orientation even if that means the label will look upside down (useful when using TTF symbol fonts to add direction markers along a line) * ``goodnessOfFit(0.5)``\ : Sets the ratio of the label that must sit inside the geometry to allow drawing the label. Works only on polygons. Provided values should span from 0 .. 1 * ``graphic-margin(10)``\ : Pixels between the stretched graphic and the text, applies when graphic stretching is in use * ``graphic-resize(true)``\ : Stretches the graphic below a label to fit the label size. Possible values are 'stretch', 'proportional'. * ``group(false)``\ : If true, geometries with the same labels are grouped and considered a single entity to be labeled. This allows to avoid or control repeated labels * ``labelAllGroup(false)``\ : When false, only the biggest geometry in a group is labeled (the biggest is obtained by merging, when possible, the original geometries). When true, also the smaller items in the group are labeled. Works only on lines at the moment. * ``repeat(0)``\ : When positive it's the desired distance between two subsequent labels on a "big" geometry. Works only on lines at the moment. If zero only one label is drawn no matter how big the geometry is * ``maxAngleDelta(90)``\ : When drawing curved labels, max allowed angle between two subsequent characters. Higher angles may cause disconnected words or overlapping characters * ``maxDisplacement(0)``\ : The distance, in pixel, a label can be displaced from its natural position in an attempt to find a position that does not conflict with already drawn labels. * ``minGroupDistance(3)``\ : Minimum distance between two labels in the same label group. To be used when both displacement and repeat are used to avoid having two labels too close to each other * ``partials(true)``\ : Option to truncate labels placed on the border of the ``displayArea`` (display partial labels) * ``polygonAlign(true)``\ : Option overriding manual rotation to align label rotation automatically for polygons. * ``spaceAround(50)``\ : The minimum distance between two labels, in pixels * ``charSpacing(0)``\ : The extra space between characters, in pixels. Can be negative. * ``wordSpacing(0)``\ : The extra space between words, in pixels. Must be zero or positive. * ``underlineText(true)``\ : When true instructs the renderer to underline labels * ``strikethroughText(true)``\ : When true instructs the renderer to ``strikethrough`` labels * ``kerning(true)``\ : When true enables text kerning (adjustment of space between characters to get a more compact and readable layout) * ``displacementMode``\ : Comma separated list of label displacement directions for point/polygon labels (used along with ``maxDisplacement``). The indicated directions will be tried in turn. Valid values are cardinal directions abbreviations, in particular, N, W, E, S, NW, NE, SW, SE. * ``fontShrinkSizeMin(0)``\ : lower font size limit that could be used when rendering the label. When set (to a positive value) the rendering process will be applied iteratively by decreasing the initial font size by 1 unit until an acceptable placement location is found or the specified value is reached. Should be set to a value greater than 0 and lower than the symbolizer's font size otherwise it is ignored. * ``graphicPlacement(label)``: placement of the graphic found in the text symbolizer, compared to the associated label. If using ``label`` (default value) the graphic is centered with the label. If using ``independet`` the graphic is placed relative to label point instead, and the graphic anchor/offset are used to position it, while the label retains its own anchor/offset related to the label point. Raster Symbolizer ''''''''''''''''' Used to control the rendering of raster data with full "color map" control. * GeoServer SLD cookbook :geoserver:`Rasters ` StyleVisitor ^^^^^^^^^^^^ Just like with the ``FilterVisitor`` interface we are going to use these implementation to navigate over a nested data structure and either copy what we see, or modify it as we go. While the ``StyleVisitor`` interface will let modify a style in place we have never found that to be a good idea (at best opening your code up to magic threading issues with what is probably a very active rendering thread). The generic ``StyleVisitor`` interface is everything you would expect from the Gang of Four ``Visitor`` pattern, it has a visit methods one for each significant interface in a ``Style`` object. To use a ``StyleVisitor`` pass it to a ``Style`` (or any style object) using the accepts method:: style.accepts( styleVisitor ); You will find that not all ``Style`` objects accept a ``StyleVisitor``; as an example ``Font`` does not. This is not really a problem - but it is something to keep in mind when writing your own visitor. **Ready to Use Implementations** There are a number of ready to use implementations; while we have provided some examples on this page please explore what is available in the library - you can do this quickly by checking the javadocs. * ``StyleAttributeExtractor`` - return all the attributes mentioned by this style; used by the renderer when constructing a Query * ``DuplicatingStyleVisitor`` - return a copy of the style * ``RescaleStyleVisitor`` - return a copy of the style modified for display at a different scale. **Implementation Tips** If you are used to simple visitors on list like data structures you are in for a surprise - ``StyleVisitor`` does not navigate the ``Style`` object structure on its own you are going to have to do the work.:: class YourStyleVisitor implements StyleVisitor { ... public void visit(Halo halo) { // do your work here // make sure you visit the "child" objects if( halo.getFill() != null ) halo.getFill().accepts( this ); if( halo.getRadius() != null ) halo.getRadius().accepts( this ); } ... } We should have an ``AbstractStyleVisitor`` for you to start from; perhaps you would like to write it for us? DuplicatingStyleVisitor ''''''''''''''''''''''' ``DuplicatingStyleVisitor`` will copy any style object; it keeps track of what is copied using an internal stack (this means it is not thread safe!).:: DuplicatingStyleVisitor xerox = new DuplicatingStyleVisitor(); style.accepts( xerox ); Style copy = (Style) xerox.getCopy(); Please note this works for everything:: DuplicatingStyleVisitor xerox = new DuplicatingStyleVisitor(); lineSymbolizer.accepts( xerox ); LineSymbolizer copy = (LineSymbolizer) xerox.getCopy(); RescaleStyleVisitor ''''''''''''''''''' ``RescaleStyleVisitor`` can be used to scale up a provided style; something that is useful when printing. The SLD specification is pretty careful about working with pixels at all times (this is annoying when you switch to 300 DPI).:: RescaleStyleVisitor scale = new RescaleStyleVisitor(5.0); style.accepts( scale ); Style bigger = (Style) scale.getCopy(); Please note that this also returns a copy; while you could modify a style in place using a visitor we find that life is too short for threading issues.:: RescaleStyleVisitor scale = new RescaleStyleVisitor(5.0); lineSymbolizer.accepts( scale ); LineSymbolizer bigger = (LineSymbolizer) scale.getCopy();