javajavafxjfreechartgeotoolsjfreechart-fx

Problem with resizing a canvas with FXGraphics2D and StreamingRenderer


I'm in the same situation as here : https://sourceforge.net/p/geotools/mailman/message/35977998/

I am working on a Maps Application using geotools (WMS + WFS for grids) and resizing my JavaFX Canvas works well when I am reducing the size of the canvas, but a part of the image is not rendered when I expend my window (the canvas is expended too).

Is there a solution ?

I'm posting the same example as the one in the link above :

import java.awt.Color;
import java.awt.Rectangle;

import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.jfree.fx.FXGraphics2D;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class ResizingTest extends Application {

    @Override
    public void start(Stage stage) {

        Canvas canvas = new Canvas(640, 480);
        BorderPane root = new BorderPane(canvas);
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();

        // Create bindings for resizing.
        canvas.widthProperty().bind(root.widthProperty());
        canvas.heightProperty().bind(root.heightProperty());

        SimpleFeatureTypeBuilder lineFeatureTypeBuilder = new
SimpleFeatureTypeBuilder();
        lineFeatureTypeBuilder.setName("LineFeatureType");
        lineFeatureTypeBuilder.setCRS(DefaultGeographicCRS.WGS84);
        lineFeatureTypeBuilder.add("the_geom", LineString.class,
DefaultGeographicCRS.WGS84);
        SimpleFeatureType lineFeatureType = lineFeatureTypeBuilder.buildFe
atureType();
        SimpleFeatureBuilder lineFeatureBuilder = new
SimpleFeatureBuilder(lineFeatureType);

        DefaultFeatureCollection lines = new DefaultFeatureCollection();

        Coordinate[][] cs = {
                { new Coordinate(-1, 42), new Coordinate(4, 46) },
                { new Coordinate(-1, 46), new Coordinate(4, 42) }
        };

        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFa
ctory();
        for(Coordinate [] c : cs) {
            LineString line = geometryFactory.createLineString(c);
            lineFeatureBuilder.add(line);
            SimpleFeature feature = lineFeatureBuilder.buildFeature(null);
            lines.add(feature);
        }

        MapContent map = new MapContent();
        Style style = SLD.createLineStyle(Color.RED, 1);
        Layer layer = new FeatureLayer(lines, style);
        map.addLayer(layer);
        //map.getViewport().setBounds(new ReferencedEnvelope(-1, 4, 42, 46,
DefaultGeographicCRS.WGS84));

        AnimationTimer loop = new AnimationTimer() {
            @Override
            public void handle(long now) {
                GraphicsContext g = canvas.getGraphicsContext2D();
                FXGraphics2D graphics = new FXGraphics2D(g);
                graphics.setBackground(java.awt.Color.BLUE);
                Rectangle rectangle = new Rectangle( (int)
canvas.getWidth(), (int) canvas.getHeight());
                graphics.clearRect(0, 0, (int) rectangle.getWidth(), (int)
rectangle.getHeight());
                graphics.drawRect(100, 100, 100, 100);
                map.getViewport().setScreenArea(rectangle); // Necessary ?
                StreamingRenderer renderer = new StreamingRenderer();
                renderer.setMapContent(map);
                renderer.paint(graphics, rectangle,
map.getViewport().getBounds());
                System.out.println("ScreenArea: " +
map.getViewport().getScreenArea() + " - Viewport: " +
map.getViewport().getBounds());
            }
        };
        loop.start();

    }

    public static void main(String[] args) {
        launch(args);
    }

}

When we expand the window, a part of the canvas is not rendered anymore, resulting on a "correct" cross (because it still goes from upper left to lower right corner), but cropped ! Anything drawn on the cropped part is not rendered

enter image description here

Edit :

I am not trying to make the canvas resizable, it already is (Proof : The cross goes from upper left to lower right pixel of canvas). The real issue is the rendering of the map that is cropped (we can't see the full cross).


Solution

  • I finally found a fix ! As mentionned in geotools' StreamingRenderer code :

    "the way this thing is built is a mess if you try to use it in a multithreaded environment"

    Avoid using StreamingRenderer in an AnimationLoop, Platform.runLater(), etc... I called the draw function everytime the map was updated instead, and it works as expected ! :)