Geotools Sld TextSymbolizer рисует текст не в том месте

я использую geotools GTRenderer в качестве Tileserver и имею файл SLD для стилизации (взято отсюда https://docs.geoserver.org/stable/en/user/styling/sld/cookbook/points.html#point-with-styled-label):

<StyledLayerDescriptor version="1.0.0"
                   xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd"
                   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">
<NamedLayer>
    <Name>WorldCities</Name>
    <UserStyle>
        <Name>Default Styler</Name>
        <FeatureTypeStyle>
            <Name>name</Name>
            <Rule>
                <PointSymbolizer>
                    <Graphic>
                        <Mark>
                            <WellKnownName>circle</WellKnownName>
                            <Fill>
                                <CssParameter name="fill">#FF0000</CssParameter>
                            </Fill>
                        </Mark>
                        <Size>6</Size>
                    </Graphic>
                </PointSymbolizer>
                <TextSymbolizer>
                    <Label>
                        <ogc:PropertyName>nameascii</ogc:PropertyName>
                    </Label>
                    <Font>
                        <CssParameter name="font-family">Arial</CssParameter>
                        <CssParameter name="font-size">12</CssParameter>
                        <CssParameter name="font-style">normal</CssParameter>
                        <CssParameter name="font-weight">bold</CssParameter>
                    </Font>
                    <LabelPlacement>
                        <PointPlacement>
                            <AnchorPoint>
                                <AnchorPointX>0.5</AnchorPointX>
                                <AnchorPointY>0.0</AnchorPointY>
                            </AnchorPoint>
                            <Displacement>
                                <DisplacementX>0</DisplacementX>
                                <DisplacementY>5</DisplacementY>
                            </Displacement>
                        </PointPlacement>
                    </LabelPlacement>
                    <Fill>
                        <CssParameter name="fill">#000000</CssParameter>
                    </Fill>
                </TextSymbolizer>

            </Rule>
        </FeatureTypeStyle>
    </UserStyle>
</NamedLayer>

PointSymbolizer работает, и я получаю одну точку в нужном месте, но текстовый символ создает сотни меток:

Ошибка

В этом образце выходных данных место Саутенд-он-Си — единственное, которое я ожидаю отобразить.

Любая идея, что может отличаться между точкой и textsymbolizer?

Спасибо за любую помощь

Отредактируйте код, который я использую:

private static Style loadStyleFromXml(String path) throws Exception {
    StyleFactory factory = CommonFactoryFinder.getStyleFactory();
    URL resource = new File(path).toURI().toURL();
    SLDParser stylereader = new SLDParser( factory, resource);
    Style styles[] = stylereader.readXML();
    return styles[0];
}
private static FeatureSource<SimpleFeatureType, SimpleFeature> readShapefile(String path) throws IOException {
    File file = new File(path);
    Map<String, Object> filemap = new HashMap<>();
    filemap.put("url", file.toURI().toURL());

    DataStore dataStore = DataStoreFinder.getDataStore(filemap);
    String typeName = dataStore.getTypeNames()[0];
    FeatureSource<SimpleFeatureType, SimpleFeature> source = dataStore.getFeatureSource(typeName);
    SimpleFeatureType schema = source.getSchema();
    return source;
}

Метод рендеринга плитки:

public synchronized byte[] renderRasterTile(int x, int y, int z){
    ReferencedEnvelope tileBounds = WebMercatorTileFactory.getExtentFromTileName(new OSMTileIdentifier(x, y, new WebMercatorZoomLevel(z), "custom"));
    try {
        tileBounds = tileBounds.transform(CRS.decode("EPSG:3857"),true);
    } catch (Exception e) {
        logger.error("Unable to transfrom coords",e);
        throw new RuntimeException(e);
    }
    BufferedImage image = new BufferedImage(tilePixelSize.width, tilePixelSize.height, BufferedImage.TYPE_INT_RGB);

    Graphics2D gr = image.createGraphics();
    gr.setPaint(new Color(0,0,0, (float) 0.1));
    gr.fill(tilePixelSize);

    try {
        renderer.paint(gr, tilePixelSize, tileBounds);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write( image, "png", baos );
        baos.flush();
        byte[] imageInByte = baos.toByteArray();
        baos.close();
        return imageInByte;
    } catch (IOException e) {
        logger.error("Unable to render tile",e);
        throw new RuntimeException(e);
    }
}
public MapContent setupMap(){
MapContent map = new MapContent();
map.setTitle("WorldMap");

FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = readShapefile("cities.shp");

Style style = loadStyleFromXml("cities.sld");
Layer layer = new FeatureLayer(featureSource, style,"cities");
map.addLayer(layer);
return map;
}

Шейп-файл, который я использовал, можно скачать здесь:

https://www.naturalearthdata.com/downloads/10m-culture-vectors/10m-populated-places/


person Ludi    schedule 16.02.2021    source источник
comment
Ваш SLD выглядит нормально, нам нужно увидеть код, который вы используете, поскольку многие имена встречаются 3 или 4 раза в разных местах, похоже, что добавляется слишком много слоев.   -  person Ian Turton    schedule 17.02.2021
comment
Спасибо за ваше время, я добавил код   -  person Ludi    schedule 17.02.2021
comment
Проблема, кажется, связана с EPSG: 3857, если я отображаю карту как jmap, она выглядит нормально. Преобразование EPSG:3857 работает для моих линий страны. Я попробовал shapfile как в WGS84, так и в EPSG:3857.   -  person Ludi    schedule 17.02.2021


Ответы (1)


Трудно быть уверенным, так как в вашем коде отсутствуют некоторые элементы, но я получаю разумные результаты из этого кода:

   try {
        StreamingRenderer renderer = new StreamingRenderer();
        MapViewport viewport = new MapViewport();
        viewport.setBounds(tileBounds);
        map.setViewport(viewport);
        renderer.setMapContent(map);
        renderer.paint(gr, tilePixelSize, tileBounds);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write( image, "png", baos );
        baos.flush();
        byte[] imageInByte = baos.toByteArray();
        baos.close();
        return imageInByte;
    } catch (IOException e) {
        System.err.println("Unable to render tile "+e);
        throw new RuntimeException(e);
    }

Ключевая часть — это то, где я устанавливаю границы области просмотра для рендерера:

        MapViewport viewport = new MapViewport();
        viewport.setBounds(tileBounds);
        map.setViewport(viewport);
        renderer.setMapContent(map);

в противном случае рендерер по-прежнему использует WGS84 (CRS слоя, при условии, что ничего другого не установлено), в то время как ваши границы находятся в EPSG: 3857, поэтому весь мир рисуется.

Для значений X=16, Y=10 и Z=5 я получаю эту плитку:

введите здесь описание изображения

Обновлять

При дальнейшем расследовании, как вы комментируете, возникает проблема, если вы рисуете две плитки подряд из одного средства визуализации, что, я уверен, вы сможете сделать. Таким образом, это похоже на ошибку в рендерере, который не очищает кеш меток, когда он начинает рисовать новую область. Не стесняйтесь опубликовать сообщение об ошибке в JIRA.

На данный момент вы можете обойти это, предоставив свой собственный LabelCache и очищая его каждый раз, когда вы вызываете paint.

GTRenderer renderer = new StreamingRenderer();
LabelCache cache = new LabelCacheImpl();
private void setup(){
    Map<Object, Object> hints = new HashMap<>();
    hints.put(StreamingRenderer.LABEL_CACHE_KEY, cache);
    renderer.setRendererHints(hints);
}

а затем добавить

cache.clear(); 

в методе renderRasterTile, кажется, работает для меня.

person Ian Turton    schedule 17.02.2021
comment
Спасибо за расследование, если я использую новый рендерер, результаты выглядят нормально. Я думаю, что если вы используете один и тот же рендерер для рендеринга x16,y10,z5, затем x16,y11,z5 и снова первого, вы сможете увидеть ошибку. Я использую версию 24.2 - person Ludi; 17.02.2021
comment
Я могу воспроизвести это с помощью mapviewport и без него. - person Ludi; 17.02.2021
comment
Да, видовой экран был отвлекающим маневром - person Ian Turton; 17.02.2021
comment
очистил кеш, решил, спасибо, открою задачу в jira - person Ludi; 17.02.2021