I'm working in Java with the GeoTools library in sld style format. I encountered a problem that I can not solve for 3 days already.

My task is to render font-size of the labels according to the current scale. Accodring to geoserver docs and the common solution I try to work with categorize function.

 <CssParameter name="font-size">
        <ogc:Function name="Categorize">
          <!-- Value to transform -->
          <ogc:Function name="env">
          <!-- Output values and thresholds -->
          <!-- Ranges: -->
          <!-- [scale <= 300, font 12] -->
          <!-- [scale 300 - 2500, font 10] -->
          <!-- [scale > 2500, font 8] -->

But I've faced to the issues:

  • For rendering labels it uses only the first font-size parameter and ignores the second and the third.
  • It renders the labels only at it's own scale, no matter what I put into the first scale and the second scale argument. I suppose it renders the labels when propper polygons being renderred.

here is my sld code:

<CssParameter xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/CssParameter.xsd" xmlns:se="http://www.opengis.net/se">

building-polygon building-polygon

      <se:Name>Single symbol</se:Name>
          <se:CssParameter name="fill">#cbcbc9</se:CssParameter>
          <se:CssParameter name="fill-opacity">0.79</se:CssParameter>
          <se:CssParameter name="stroke">#cbcbc9</se:CssParameter>
          <se:CssParameter name="stroke-linejoin">bevel</se:CssParameter>


            <ogc:Function name="centroid">


            <CssParameter name="font-family">Arial</CssParameter>
            <CssParameter name="font-size">                 

                <ogc:Function name="Categorize">                                
                    <ogc:Function name="env">


            <CssParameter name="font-style">normal</CssParameter>
            <CssParameter name="font-weight">normal</CssParameter>
            <CssParameter name="fill">#2a2a35</CssParameter>
        <!-- <VendorOption name="polygonAlign">ortho</VendorOption> 
             <VendorOption name="autoWrap">60</VendorOption> -->


And here is my Java code:

package TestGeoToolsMvn;

import java.awt.Rectangle;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;

import org.geotools.data.CachingFeatureSource;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.SLD;
import org.geotools.styling.SLDParser;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.swing.dialog.JExceptionReporter;
import org.jfree.fx.FXGraphics2D;

import javafx.scene.input.*;
import javafx.application.Platform;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.util.Duration;

public class MapCanvas {
    private Canvas canvas;
    private MapContent map;
    private GraphicsContext graphicsContext;
    static StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();

    private boolean repaint = true;
    private double baseDrageX;
    private double baseDrageY;
    private static final double PAINT_HZ = 50.0;

    //============================= CONSTANT ===============================
    final int INDEX_FROM = 1; //inex for hide and show layers methods
    final String HOME_DIR_1 = "src\\main\\resources\\";

    java.awt.Color colorBackground = new java.awt.Color(250, 250, 250);

    public MapCanvas(int width, int height) {

        canvas = new Canvas(width, height);
        graphicsContext = canvas.getGraphicsContext2D();
        map = new MapContent();





    public Node getCanvas() {
        return canvas;

    private void initLayer(String pathToShp) {
        try {
            FileDataStore store = FileDataStoreFinder.getDataStore(this.getClass().getClassLoader().getResource(pathToShp));
            SimpleFeatureSource featureSource = store.getFeatureSource();

            ((ShapefileDataStore) store).setCharset(Charset.defaultCharset());
            Style style = createFromSLD(pathToShp);
            FeatureLayer layer = new FeatureLayer(featureSource, style);
            System.out.println("СИСТЕМНОЕ СООБЩЕНИЕ: Создан слой для " + pathToShp);
            System.out.println("СИСТЕМНОЕ СООБЩЕНИЕ: Загружен слой: " + pathToShp);

            map.getViewport().setScreenArea(new Rectangle((int) canvas.getWidth(), (int) canvas.getHeight()));
        } catch (IOException e) {

    private void drawMap(GraphicsContext gc) {
        if (repaint) {
            repaint = false;
            StreamingRenderer draw = new StreamingRenderer();
            FXGraphics2D graphics = new FXGraphics2D(gc);
            graphics.clearRect(0, 0, (int) canvas.getWidth(), (int) canvas.getHeight());
            Rectangle rectangle = new Rectangle((int) canvas.getWidth(), (int) canvas.getHeight());

            draw.paint(graphics, rectangle, map.getViewport().getBounds());
        else {return;}

    private void initEvent() {
        canvas.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {

            public void handle(MouseEvent e) {
                baseDrageX = e.getSceneX();
                baseDrageY = e.getSceneY();

        canvas.addEventFilter(MouseEvent.DRAG_DETECTED, e -> {
            System.out.println("Drag starts");

        canvas.addEventHandler(MouseDragEvent.MOUSE_DRAG_OVER, new EventHandler<MouseDragEvent>() {
            public void handle(MouseDragEvent e) {
                hideLayers(INDEX_FROM); //Hide layers for performance achievments

                double difX = e.getSceneX() - baseDrageX;
                double difY = e.getSceneY() - baseDrageY;
                baseDrageX = e.getSceneX();
                baseDrageY = e.getSceneY();
                DirectPosition2D newPos = new DirectPosition2D(difX, difY);
                DirectPosition2D result = new DirectPosition2D();
                map.getViewport().getScreenToWorld().transform(newPos, result);
                ReferencedEnvelope env = new ReferencedEnvelope(map.getViewport().getBounds());
                env.translate(env.getMinimum(0) - result.x, env.getMaximum(1) - result.y);

        canvas.addEventHandler(MouseDragEvent.MOUSE_DRAG_RELEASED, new EventHandler<MouseDragEvent>() {
            public void handle(MouseDragEvent e) {
                showLayers(INDEX_FROM, map.getViewport().getBounds()); //Show layers for performance achievments


         * double clicks to restore to original map
        canvas.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
            public void handle(MouseEvent t) {
                if (t.getClickCount() > 1) {
         * scroll for zoom in and out
        canvas.addEventHandler(ScrollEvent.SCROLL, new EventHandler<ScrollEvent>() {
            private int scrollCounter = 0; //counter for creating self-made SCROLL_FINISHED event

            public void handle(ScrollEvent e) {
                hideLayers(INDEX_FROM); //Hide layers for performance achievments

                ReferencedEnvelope envelope = map.getViewport().getBounds();
                double percent = (e.getDeltaY() / canvas.getWidth()) * 4;
                double width = envelope.getWidth();
                double height = envelope.getHeight();
                double deltaW = width * percent;
                double deltaH = height * percent;
                envelope.expandBy(deltaW, deltaH);

                scrollCounter ++;

                Thread th = new Thread(() -> {
                    try {
                        Platform.runLater(() -> {
                            if(scrollCounter == 1) {
                                System.out.println("Scroll ended!");
                                showLayers(INDEX_FROM, envelope); //Show layers for performance achievments

                    } catch (Exception e1) {


    private void initPaintThread() {
        ScheduledService<Boolean> svc = new ScheduledService<Boolean>() {
            protected Task<Boolean> createTask() {
                return new Task<Boolean>() {
                    protected Boolean call() {
                        Platform.runLater(() -> {
                        return true;
        svc.setPeriod(Duration.millis(1000.0 / PAINT_HZ));

    public void hideLayers(int indexFrom) {
        //Hide layers for performance achievments
        List<Layer> layers;
        layers = map.layers();
        for (indexFrom = indexFrom; indexFrom < layers.size(); indexFrom++){
            Layer l = layers.get(indexFrom);
            System.out.println("СИСТЕМНОЕ СООБЩЕНИЕ: скрыт слой - " + l.toString());

    public void showLayers(int indexFrom, ReferencedEnvelope envelope) {
        //Show layers for performance achievments
        List<Layer> layers;
        layers = map.layers();
        for (indexFrom = indexFrom; indexFrom < layers.size(); indexFrom++){
            Layer l = layers.get(indexFrom);
            System.out.println("СИСТЕМНОЕ СООБЩЕНИЕ: показан слой - " + l.toString());

    protected void doSetDisplayArea(ReferencedEnvelope envelope) {
        repaint = true;

     * Create a Style object from a definition in a SLD document
    private Style createFromSLD(String pathToSLD) {
        try {
            pathToSLD = HOME_DIR_1 + pathToSLD.replaceAll(".shp",".sld");
            SLDParser stylereader = new SLDParser(styleFactory, pathToSLD);
            //Style[] style = stylereader.readXML();
            Style[] style = stylereader.readXML();
            return style[0];

        } catch (Exception e) {
            JExceptionReporter.showDialog(e, "СИСТЕМНОЕ СООБЩЕНИЕ: не удалось импортировать стиль из файла");

        return null;

enter image description here

  • Are you sure that cssparameters will take a function? If you are, you need to add the code you are using
    – Ian Turton
    Dec 15, 2017 at 16:24
  • Yes, because there is the same code in the official geoserver docs. docs.geoserver.org/stable/en/user/styling/sld/tipstricks/… And I can set font-size by categorize function, but it uses only one of the function's input argument. Dec 16, 2017 at 17:08
  • I think may be there is some problems with wms_scale_denominator interpritation? In fact I don't use Geoserver and maybe that's why java geotools library can't get propper current scale with wms_scale_denominator. How can I do the same but without using wms_scale_denominator? Dec 16, 2017 at 17:16
  • Please add your code to the question.
    – Ian Turton
    Dec 16, 2017 at 17:33
  • Done. I have to notice that this is my test App for studying. Code was added to the topic starter message. Dec 16, 2017 at 17:48

1 Answer 1


Functions are supported in all CSSParameter (by specification), but our code is not working because the wms_scale_denominator environment variable is added by some GeoServer code in the WMS machinery, and it's thus not available in your stand alone GeoTools client.

For reference, here is the GeoServer code responsible for the creation of wms_scale_denominator: https://github.com/geoserver/geoserver/blob/6058e38a0c74247ee6fea3db81c3aecbca1f40d2/src/wms/src/main/java/org/geoserver/wms/RenderingVariables.java#L21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.