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">
<ogc:Literal>wms_scale_denominator</ogc:Literal>
</ogc:Function>
<!-- Output values and thresholds -->
<!-- Ranges: -->
<!-- [scale <= 300, font 12] -->
<!-- [scale 300 - 2500, font 10] -->
<!-- [scale > 2500, font 8] -->
<ogc:Literal>12</ogc:Literal>
<ogc:Literal>300</ogc:Literal>
<ogc:Literal>10</ogc:Literal>
<ogc:Literal>2500</ogc:Literal>
<ogc:Literal>8</ogc:Literal>
</ogc:Function>
</CssParameter>
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:Rule>
<se:Name>Single symbol</se:Name>
<se:MinScaleDenominator>0</se:MinScaleDenominator>
<se:MaxScaleDenominator>100000</se:MaxScaleDenominator>
<se:PolygonSymbolizer>
<se:Fill>
<se:CssParameter name="fill">#cbcbc9</se:CssParameter>
<se:CssParameter name="fill-opacity">0.79</se:CssParameter>
</se:Fill>
<se:Stroke>
<se:CssParameter name="stroke">#cbcbc9</se:CssParameter>
<se:CssParameter name="stroke-linejoin">bevel</se:CssParameter>
</se:Stroke>
</se:PolygonSymbolizer>
<TextSymbolizer>
<Geometry>
<ogc:Function name="centroid">
<ogc:PropertyName>the_geom</ogc:PropertyName>
</ogc:Function>
</Geometry>
<Label>
<ogc:PropertyName>A_HSNMBR</ogc:PropertyName>
</Label>
<Font>
<CssParameter name="font-family">Arial</CssParameter>
<CssParameter name="font-size">
<ogc:Function name="Categorize">
<ogc:Function name="env">
<ogc:Literal>wms_scale_denominator</ogc:Literal>
</ogc:Function>
<ogc:Literal>10</ogc:Literal>
<ogc:Literal>300</ogc:Literal>
<ogc:Literal>6</ogc:Literal>
<ogc:Literal>2500</ogc:Literal>
<ogc:Literal>1</ogc:Literal>
</ogc:Function>
</CssParameter>
<CssParameter name="font-style">normal</CssParameter>
<CssParameter name="font-weight">normal</CssParameter>
</Font>
<LabelPlacement>
<LinePlacement>
<PointPlacement>
<AnchorPoint>
<AnchorPointX>0.5</AnchorPointX>
<AnchorPointY>0.5</AnchorPointY>
</AnchorPoint>
</PointPlacement>
</LinePlacement>
</LabelPlacement>
<Fill>
<CssParameter name="fill">#2a2a35</CssParameter>
</Fill>
<!-- <VendorOption name="polygonAlign">ortho</VendorOption>
<VendorOption name="autoWrap">60</VendorOption> -->
</TextSymbolizer>
</se:Rule>
</se:FeatureTypeStyle>
</UserStyle>
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();
map.setTitle("Quickstart");
initLayer("testGeoToolsMvn\\110m_cultural\\data\\boundary-polygon.shp");
initLayer("testGeoToolsMvn\\110m_cultural\\data\\landuse-polygon.shp");
initLayer("testGeoToolsMvn\\110m_cultural\\data\\settlement-polygon.shp");
initLayer("testGeoToolsMvn\\110m_cultural\\data\\building-polygon.shp");
initLayer("testGeoToolsMvn\\110m_cultural\\data\\railway-platform-polygon.shp");
initLayer("testGeoToolsMvn\\110m_cultural\\data\\vegetation-polygon.shp");
initLayer("testGeoToolsMvn\\110m_cultural\\data\\water-polygon.shp");
initLayer("testGeoToolsMvn\\110m_cultural\\data\\water-line.shp");
initLayer("testGeoToolsMvn\\110m_cultural\\data\\railway-line.shp");
initLayer("testGeoToolsMvn\\110m_cultural\\data\\highway-line.shp");
drawMap(graphicsContext);
initEvent();
initPaintThread();
}
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) {
e.printStackTrace();
}
}
private void drawMap(GraphicsContext gc) {
if (repaint) {
repaint = false;
StreamingRenderer draw = new StreamingRenderer();
draw.setMapContent(map);
FXGraphics2D graphics = new FXGraphics2D(gc);
graphics.setBackground(colorBackground);
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>() {
@Override
public void handle(MouseEvent e) {
baseDrageX = e.getSceneX();
baseDrageY = e.getSceneY();
e.consume();
}
});
canvas.addEventFilter(MouseEvent.DRAG_DETECTED, e -> {
canvas.startFullDrag();
System.out.println("Drag starts");
});
canvas.addEventHandler(MouseDragEvent.MOUSE_DRAG_OVER, new EventHandler<MouseDragEvent>() {
@Override
public void handle(MouseDragEvent e) {
System.out.println("Dragging");
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);
doSetDisplayArea(env);
e.consume();
}
});
canvas.addEventHandler(MouseDragEvent.MOUSE_DRAG_RELEASED, new EventHandler<MouseDragEvent>() {
@Override
public void handle(MouseDragEvent e) {
System.out.println("Realesed");
showLayers(INDEX_FROM, map.getViewport().getBounds()); //Show layers for performance achievments
e.consume();
}
});
/*
* double clicks to restore to original map
*/
canvas.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent t) {
if (t.getClickCount() > 1) {
doSetDisplayArea(map.getMaxBounds());
}
t.consume();
}
});
/*
* 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
@Override
public void handle(ScrollEvent e) {
System.out.println("Scrolled");
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);
doSetDisplayArea(envelope);
e.consume();
scrollCounter ++;
Thread th = new Thread(() -> {
try {
Thread.sleep(1000);
Platform.runLater(() -> {
if(scrollCounter == 1) {
System.out.println("Scroll ended!");
showLayers(INDEX_FROM, envelope); //Show layers for performance achievments
}
scrollCounter--;
});
} catch (Exception e1) {
e1.printStackTrace();
}
});
th.setDaemon(true);
th.start();
}
});
}
private void initPaintThread() {
ScheduledService<Boolean> svc = new ScheduledService<Boolean>() {
protected Task<Boolean> createTask() {
return new Task<Boolean>() {
protected Boolean call() {
Platform.runLater(() -> {
drawMap(graphicsContext);
});
return true;
}
};
}
};
svc.setPeriod(Duration.millis(1000.0 / PAINT_HZ));
svc.start();
}
public void hideLayers(int indexFrom) {
//Hide layers for performance achievments
List<Layer> layers;
layers = map.layers();
for (indexFrom = indexFrom; indexFrom < layers.size(); indexFrom++){
layers.get(indexFrom).setVisible(false);
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++){
layers.get(indexFrom).setVisible(true);
doSetDisplayArea(envelope);
Layer l = layers.get(indexFrom);
System.out.println("СИСТЕМНОЕ СООБЩЕНИЕ: показан слой - " + l.toString());
}
}
protected void doSetDisplayArea(ReferencedEnvelope envelope) {
map.getViewport().setBounds(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;
}
}